Skip to content

Commit

Permalink
Use the objectmatcher patch package for a generic solution to check d…
Browse files Browse the repository at this point in the history
…iffs
  • Loading branch information
pepov authored and waynz0r committed Jun 26, 2019
1 parent f6b7d4b commit 9a9c56e
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 99 deletions.
11 changes: 6 additions & 5 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ version="v1.4.7"
# revision for tag "kubernetes-1.12.3"
revision = "d082d5923d3cc0bfbb066ee5fbdea3d0ca79acf8"

[[constraint]]
branch = "master"
[[override]]
name = "github.com/banzaicloud/k8s-objectmatcher"
version = "0.1.1"

[[constraint]]
name = "github.com/mholt/caddy"
Expand Down
9 changes: 3 additions & 6 deletions pkg/controller/istio/istio_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"strings"
"time"

patch "github.com/banzaicloud/k8s-objectmatcher/patch"
"github.com/go-logr/logr"
"github.com/gofrs/uuid"
"github.com/goph/emperror"
Expand Down Expand Up @@ -64,7 +65,6 @@ import (
"github.com/banzaicloud/istio-operator/pkg/resources/pilot"
"github.com/banzaicloud/istio-operator/pkg/resources/sidecarinjector"
"github.com/banzaicloud/istio-operator/pkg/util"
objectmatch "github.com/banzaicloud/k8s-objectmatcher"
)

const finalizerID = "istio-operator.finializer.banzaicloud.io"
Expand Down Expand Up @@ -520,9 +520,6 @@ func initWatches(c controller.Controller, scheme *runtime.Scheme, watchCreatedRe
return nil
}

// Initialize object matcher
objectMatcher := objectmatch.New(logf.NewDelegatingLogger(logf.NullLogger{}))

// Initialize owner matcher
ownerMatcher := k8sutil.NewOwnerReferenceMatcher(&istiov1beta1.Istio{TypeMeta: metav1.TypeMeta{Kind: "Istio", APIVersion: "istio.banzaicloud.io/v1beta1"}}, true, scheme)

Expand Down Expand Up @@ -558,10 +555,10 @@ func initWatches(c controller.Controller, scheme *runtime.Scheme, watchCreatedRe
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
objectsEquals, err := objectMatcher.Match(e.ObjectOld, e.ObjectNew)
patchResult, err := patch.DefaultPatchMaker.Calculate(e.ObjectOld, e.ObjectNew)
if err != nil {
logger.Error(err, "could not match objects", "kind", e.ObjectOld.GetObjectKind())
} else if objectsEquals {
} else if patchResult.IsEmpty() {
return false
}
related, object, err := ownerMatcher.Match(e.ObjectNew)
Expand Down
23 changes: 18 additions & 5 deletions pkg/crds/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"fmt"
"strings"

objectmatch "github.com/banzaicloud/k8s-objectmatcher"
patch "github.com/banzaicloud/k8s-objectmatcher/patch"
"github.com/go-logr/logr"
"github.com/goph/emperror"
extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
Expand Down Expand Up @@ -194,6 +194,9 @@ func (r *CrdOperator) Reconcile(config *istiov1beta1.Istio, log logr.Logger) err
return emperror.WrapWith(err, "getting CRD failed", "kind", crd.Spec.Names.Kind)
}
if apierrors.IsNotFound(err) {
if err := patch.DefaultAnnotator.SetLastAppliedAnnotation(crd); err != nil {
log.Error(err, "Failed to set last applied annotation", "crd", crd)
}
if config.Name != "" {
crd.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
{
Expand All @@ -210,14 +213,24 @@ func (r *CrdOperator) Reconcile(config *istiov1beta1.Istio, log logr.Logger) err
return emperror.WrapWith(err, "creating CRD failed", "kind", crd.Spec.Names.Kind)
}
log.Info("CRD created")
}
if err == nil {
objectsEquals, err := objectmatch.New(log).Match(current, crd)
} else {
patchResult, err := patch.DefaultPatchMaker.Calculate(current, crd)
if err != nil {
log.Error(err, "could not match objects", "kind", crd.Spec.Names.Kind)
} else if objectsEquals {
} else if patchResult.IsEmpty() {
log.V(1).Info("CRD is in sync")
continue
} else {
log.V(1).Info("resource diffs",
"patch", string(patchResult.Patch),
"current", string(patchResult.Current),
"modified", string(patchResult.Modified),
"original", string(patchResult.Original))
}
// Need to set this before resourceversion is set, as it would constantly change otherwise

if err := patch.DefaultAnnotator.SetLastAppliedAnnotation(crd); err != nil {
log.Error(err, "Failed to set last applied annotation", "crd", crd)
}
crd.ResourceVersion = current.ResourceVersion
if _, err := crdClient.Update(crd); err != nil {
Expand Down
103 changes: 32 additions & 71 deletions pkg/k8sutil/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,14 @@ package k8sutil

import (
"context"
"errors"
"reflect"

objectmatch "github.com/banzaicloud/k8s-objectmatcher"
"github.com/banzaicloud/k8s-objectmatcher/patch"
"github.com/go-logr/logr"
"github.com/goph/emperror"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
appsv1 "k8s.io/api/apps/v1"
autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
runtimeClient "sigs.k8s.io/controller-runtime/pkg/client"
)
Expand All @@ -53,79 +48,45 @@ func Reconcile(log logr.Logger, client runtimeClient.Client, desired runtime.Obj
if err != nil && !apierrors.IsNotFound(err) {
return emperror.WrapWith(err, "getting resource failed", "kind", desiredType, "name", key.Name)
}
if apierrors.IsNotFound(err) && desiredState == DesiredStatePresent {
if err := client.Create(context.TODO(), desired); err != nil {
return emperror.WrapWith(err, "creating resource failed", "kind", desiredType, "name", key.Name)
if apierrors.IsNotFound(err) {
if desiredState == DesiredStatePresent {
if err := patch.DefaultAnnotator.SetLastAppliedAnnotation(desired); err != nil {
log.Error(err, "Failed to set last applied annotation", "desired", desired)
}
if err := client.Create(context.TODO(), desired); err != nil {
return emperror.WrapWith(err, "creating resource failed", "kind", desiredType, "name", key.Name)
}
log.Info("resource created")
}
log.Info("resource created")
}
if err == nil {
} else {
if desiredState == DesiredStatePresent {
objectsEquals, err := objectmatch.New(log).Match(current, desired)
patchResult, err := patch.DefaultPatchMaker.Calculate(current, desired)
if err != nil {
log.Error(err, "could not match objects", "kind", desiredType, "name", key.Name)
} else if objectsEquals {
} else if patchResult.IsEmpty() {
log.V(1).Info("resource is in sync")
return nil
} else {
log.V(1).Info("resource diffs",
"patch", string(patchResult.Patch),
"current", string(patchResult.Current),
"modified", string(patchResult.Modified),
"original", string(patchResult.Original))
}

switch desired.(type) {
default:
return emperror.With(errors.New("unexpected resource type"), "kind", desiredType, "name", key.Name)
case *corev1.Namespace:
ns := desired.(*corev1.Namespace)
ns.ResourceVersion = current.(*corev1.Namespace).ResourceVersion
desired = ns
case *corev1.ServiceAccount:
sa := desired.(*corev1.ServiceAccount)
sa.ResourceVersion = current.(*corev1.ServiceAccount).ResourceVersion
desired = sa
case *rbacv1.ClusterRole:
cr := desired.(*rbacv1.ClusterRole)
cr.ResourceVersion = current.(*rbacv1.ClusterRole).ResourceVersion
desired = cr
case *rbacv1.ClusterRoleBinding:
crb := desired.(*rbacv1.ClusterRoleBinding)
crb.ResourceVersion = current.(*rbacv1.ClusterRoleBinding).ResourceVersion
desired = crb
case *corev1.ConfigMap:
cm := desired.(*corev1.ConfigMap)
cm.ResourceVersion = current.(*corev1.ConfigMap).ResourceVersion
desired = cm
case *corev1.Service:
svc := desired.(*corev1.Service)
svc.ResourceVersion = current.(*corev1.Service).ResourceVersion
svc.Spec.ClusterIP = current.(*corev1.Service).Spec.ClusterIP
desired = svc
case *appsv1.Deployment:
deploy := desired.(*appsv1.Deployment)
deploy.ResourceVersion = current.(*appsv1.Deployment).ResourceVersion
desired = deploy
case *autoscalingv2beta1.HorizontalPodAutoscaler:
hpa := desired.(*autoscalingv2beta1.HorizontalPodAutoscaler)
hpa.ResourceVersion = current.(*autoscalingv2beta1.HorizontalPodAutoscaler).ResourceVersion
desired = hpa
case *admissionregistrationv1beta1.MutatingWebhookConfiguration:
mwc := desired.(*admissionregistrationv1beta1.MutatingWebhookConfiguration)
mwc.ResourceVersion = current.(*admissionregistrationv1beta1.MutatingWebhookConfiguration).ResourceVersion
desired = mwc
case *policyv1beta1.PodDisruptionBudget:
pdb := desired.(*policyv1beta1.PodDisruptionBudget)
pdb.ResourceVersion = current.(*policyv1beta1.PodDisruptionBudget).ResourceVersion
desired = pdb
case *appsv1.DaemonSet:
ds := desired.(*appsv1.DaemonSet)
ds.ResourceVersion = current.(*appsv1.DaemonSet).ResourceVersion
desired = ds
case *rbacv1.Role:
ds := desired.(*rbacv1.Role)
ds.ResourceVersion = current.(*rbacv1.Role).ResourceVersion
desired = ds
case *rbacv1.RoleBinding:
ds := desired.(*rbacv1.RoleBinding)
ds.ResourceVersion = current.(*rbacv1.RoleBinding).ResourceVersion
desired = ds
// Need to set this before resourceversion is set, as it would constantly change otherwise
if err := patch.DefaultAnnotator.SetLastAppliedAnnotation(desired); err != nil {
log.Error(err, "Failed to set last applied annotation", "desired", desired)
}

metaAccessor := meta.NewAccessor()
currentResourceVersion, err := metaAccessor.ResourceVersion(current)
if err != nil {
return err
}

metaAccessor.SetResourceVersion(desired, currentResourceVersion)

if err := client.Update(context.TODO(), desired); err != nil {
if apierrors.IsConflict(err) || apierrors.IsInvalid(err) {
err := client.Delete(context.TODO(), current)
Expand Down
33 changes: 23 additions & 10 deletions pkg/k8sutil/unstructured.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package k8sutil
import (
"reflect"

objectmatch "github.com/banzaicloud/k8s-objectmatcher"
patch "github.com/banzaicloud/k8s-objectmatcher/patch"
"github.com/go-logr/logr"
"github.com/goph/emperror"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -60,22 +60,35 @@ func (d *DynamicObject) Reconcile(log logr.Logger, client dynamic.Interface, des
if err != nil && !apierrors.IsNotFound(err) {
return emperror.WrapWith(err, "getting resource failed", "name", d.Name, "kind", desiredType)
}
if apierrors.IsNotFound(err) && desiredState == DesiredStatePresent {
if _, err := client.Resource(d.Gvr).Namespace(d.Namespace).Create(desired, metav1.CreateOptions{}); err != nil {
return emperror.WrapWith(err, "creating resource failed", "name", d.Name, "kind", desiredType)
if apierrors.IsNotFound(err) {
if desiredState == DesiredStatePresent {
if err := patch.DefaultAnnotator.SetLastAppliedAnnotation(desired); err != nil {
log.Error(err, "Failed to set last applied annotation", "desired", desired)
}
if _, err := client.Resource(d.Gvr).Namespace(d.Namespace).Create(desired, metav1.CreateOptions{}); err != nil {
return emperror.WrapWith(err, "creating resource failed", "name", d.Name, "kind", desiredType)
}
log.Info("resource created", "kind", d.Gvr.Resource)
}
log.Info("resource created", "kind", d.Gvr.Resource)
}
if err == nil {
} else {
if desiredState == DesiredStatePresent {
objectsEquals, err := objectmatch.New(log).Match(current, desired)
patchResult, err := patch.DefaultPatchMaker.Calculate(current, desired)
if err != nil {
log.Error(err, "could not match objects", "kind", desiredType)
} else if objectsEquals {
} else if patchResult.IsEmpty() {
log.V(1).Info("resource is in sync")
return nil
} else {
log.V(1).Info("resource diffs",
"patch", string(patchResult.Patch),
"current", string(patchResult.Current),
"modified", string(patchResult.Modified),
"original", string(patchResult.Original))
}
// Need to set this before resourceversion is set, as it would constantly change otherwise
if err := patch.DefaultAnnotator.SetLastAppliedAnnotation(desired); err != nil {
log.Error(err, "Failed to set last applied annotation", "desired", desired)
}

desired.SetResourceVersion(current.GetResourceVersion())
if _, err := client.Resource(d.Gvr).Namespace(d.Namespace).Update(desired, metav1.UpdateOptions{}); err != nil {
return emperror.WrapWith(err, "updating resource failed", "name", d.Name, "kind", desiredType)
Expand Down

0 comments on commit 9a9c56e

Please sign in to comment.