Change health check from WorkloadReference to CrossNamespaceObjectReference

This commit is contained in:
Philip Laine 2020-09-07 23:31:31 +02:00
parent d711b699bb
commit ea627e3448
5 changed files with 47 additions and 79 deletions

View File

@ -17,7 +17,6 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
"fmt"
"time" "time"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -54,7 +53,7 @@ type KustomizationSpec struct {
// A list of workloads (Deployments, DaemonSets and StatefulSets) // A list of workloads (Deployments, DaemonSets and StatefulSets)
// to be included in the health assessment. // to be included in the health assessment.
// +optional // +optional
HealthChecks []WorkloadReference `json:"healthChecks,omitempty"` HealthChecks []CrossNamespaceObjectReference `json:"healthChecks,omitempty"`
// The Kubernetes service account used for applying the kustomization. // The Kubernetes service account used for applying the kustomization.
// +optional // +optional
@ -81,25 +80,6 @@ type KustomizationSpec struct {
Validation string `json:"validation,omitempty"` Validation string `json:"validation,omitempty"`
} }
// WorkloadReference defines a reference to a Deployment, DaemonSet or StatefulSet.
type WorkloadReference struct {
// GroupKind is the type of resource being referenced.
// +required
GroupKind metav1.GroupKind `json:"groupKind"`
// Name is the name of resource being referenced.
// +required
Name string `json:"name"`
// Namespace is the namespace of resource being referenced.
// +required
Namespace string `json:"namespace"`
}
func (w WorkloadReference) String() string {
return fmt.Sprintf("%s/%s/%s", w.GroupKind.String(), w.Namespace, w.Name)
}
// ServiceAccount defines a reference to a Kubernetes service account. // ServiceAccount defines a reference to a Kubernetes service account.
type ServiceAccount struct { type ServiceAccount struct {
// Name is the name of the service account being referenced. // Name is the name of the service account being referenced.

View File

@ -24,7 +24,6 @@ type CrossNamespaceObjectReference struct {
APIVersion string `json:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referent // Kind of the referent
// +kubebuilder:validation:Enum=GitRepository
// +required // +required
Kind string `json:"kind"` Kind string `json:"kind"`

View File

@ -152,7 +152,7 @@ func (in *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
out.Interval = in.Interval out.Interval = in.Interval
if in.HealthChecks != nil { if in.HealthChecks != nil {
in, out := &in.HealthChecks, &out.HealthChecks in, out := &in.HealthChecks, &out.HealthChecks
*out = make([]WorkloadReference, len(*in)) *out = make([]CrossNamespaceObjectReference, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.ServiceAccount != nil { if in.ServiceAccount != nil {
@ -263,19 +263,3 @@ func (in *SnapshotEntry) DeepCopy() *SnapshotEntry {
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkloadReference) DeepCopyInto(out *WorkloadReference) {
*out = *in
out.GroupKind = in.GroupKind
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadReference.
func (in *WorkloadReference) DeepCopy() *WorkloadReference {
if in == nil {
return nil
}
out := new(WorkloadReference)
in.DeepCopyInto(out)
return out
}

View File

@ -79,30 +79,24 @@ spec:
description: A list of workloads (Deployments, DaemonSets and StatefulSets) description: A list of workloads (Deployments, DaemonSets and StatefulSets)
to be included in the health assessment. to be included in the health assessment.
items: items:
description: WorkloadReference defines a reference to a Deployment, description: CrossNamespaceObjectReference contains enough information
DaemonSet or StatefulSet. to let you locate the typed referenced object at cluster level
properties: properties:
groupKind: apiVersion:
description: GroupKind is the type of resource being referenced. description: API version of the referent
properties: type: string
group: kind:
type: string description: Kind of the referent
kind: type: string
type: string
required:
- group
- kind
type: object
name: name:
description: Name is the name of resource being referenced. description: Name of the referent
type: string type: string
namespace: namespace:
description: Namespace is the namespace of resource being referenced. description: Namespace of the referent
type: string type: string
required: required:
- groupKind - kind
- name - name
- namespace
type: object type: object
type: array type: array
interval: interval:
@ -139,8 +133,6 @@ spec:
type: string type: string
kind: kind:
description: Kind of the referent description: Kind of the referent
enum:
- GitRepository
type: string type: string
name: name:
description: Name of the referent description: Name of the referent

View File

@ -621,25 +621,34 @@ func (r *KustomizationReconciler) prune(kustomization kustomizev1.Kustomization,
return nil return nil
} }
func toObjMetadata(wr []kustomizev1.WorkloadReference) []object.ObjMetadata { func toObjMetadata(cr []kustomizev1.CrossNamespaceObjectReference) ([]object.ObjMetadata, error) {
oo := []object.ObjMetadata{} oo := []object.ObjMetadata{}
for _, w := range wr { for _, c := range cr {
oo = append(oo, object.ObjMetadata{ // For backwards compatibility
Name: w.Name, if c.APIVersion == "" {
Namespace: w.Namespace, c.APIVersion = "apps/v1"
GroupKind: schema.GroupKind{ }
Group: w.GroupKind.Group,
Kind: w.GroupKind.Kind, gv, err := schema.ParseGroupVersion(c.APIVersion)
}, if err != nil {
}) return []object.ObjMetadata{}, err
}
gk := schema.GroupKind{Group: gv.Group, Kind: c.Kind}
o, err := object.CreateObjMetadata(c.Namespace, c.Name, gk)
if err != nil {
return []object.ObjMetadata{}, err
}
oo = append(oo, o)
} }
return oo return oo, nil
} }
func toHealthySet(wr []kustomizev1.WorkloadReference) map[string]bool { func toHealthySet(oo []object.ObjMetadata) map[string]bool {
hs := map[string]bool{} hs := map[string]bool{}
for _, w := range wr { for _, o := range oo {
hs[w.String()] = false hs[o.String()] = false
} }
return hs return hs
} }
@ -659,31 +668,35 @@ func (r *KustomizationReconciler) checkHealth(kustomization kustomizev1.Kustomiz
return nil return nil
} }
objMetadata, err := toObjMetadata(kustomization.Spec.HealthChecks)
if err != nil {
return err
}
timeout := kustomization.GetTimeout() + (time.Second * 1) timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() defer cancel()
opts := polling.Options{PollInterval: 500 * time.Millisecond, UseCache: true} opts := polling.Options{PollInterval: 500 * time.Millisecond, UseCache: true}
healthySet := toHealthySet(kustomization.Spec.HealthChecks) healthySet := toHealthySet(objMetadata)
eventsChan := r.Poller.Poll(ctx, toObjMetadata(kustomization.Spec.HealthChecks), opts) eventsChan := r.Poller.Poll(ctx, objMetadata, opts)
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
notHealthy := filterHealthSet(healthySet, false) notHealthy := filterHealthSet(healthySet, false)
return fmt.Errorf("Health check timeout for [%v]", strings.Join(notHealthy, ", ")) return fmt.Errorf("Health check timed out for [%v]", strings.Join(notHealthy, ", "))
case e := <-eventsChan: case e := <-eventsChan:
switch e.EventType { switch e.EventType {
case event.ResourceUpdateEvent: case event.ResourceUpdateEvent:
id := fmt.Sprintf("%s/%s/%s", e.Resource.Identifier.GroupKind.String(), e.Resource.Identifier.Namespace, e.Resource.Identifier.Name)
if e.Resource.Status == status.CurrentStatus { if e.Resource.Status == status.CurrentStatus {
healthySet[id] = true healthySet[e.Resource.Identifier.String()] = true
r.Log.WithValues( r.Log.WithValues(
strings.ToLower(kustomization.Kind), strings.ToLower(kustomization.Kind),
fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()), fmt.Sprintf("%s/%s", kustomization.GetNamespace(), kustomization.GetName()),
).Info(fmt.Sprintf("Health check passed for %s", id)) ).Info(fmt.Sprintf("Health check passed for %s", e.Resource.Identifier.String()))
} else { } else {
healthySet[id] = false healthySet[e.Resource.Identifier.String()] = false
} }
case event.ErrorEvent: case event.ErrorEvent:
return e.Error return e.Error