diff --git a/docs/user-guide.md b/docs/user-guide.md index cfb123a5..2d8b20e2 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -52,49 +52,30 @@ resources and resource templates. The matches can be added to the config as pair ##### Correlation by group of fields (apiVersion, kind, namespace and name) -When there is no manual match for a CR the command will try to match a template -for the resource by looking at the 4-tuple: apiVersion, kind, namespace and -name. +When there is no manual match for a CR the command will try to match a template for the resource by looking at the +4-tuple: apiVersion, kind, namespace and name . The Correlation is based on which fields in the templates that are not +user-variable. Templates get matched to resources based on all the features from the 4-tuple that are declared fixed ( +not user-variable) in the templates. -For each resource the group correlation will be finding a template which -matches the largest number of fields from this group: apiVersion, kind, -namespace, name. This is done in the following order, with the first match -taken: +For example a template with a fixed namespace, kind, name and templated (user-variable) apiVersion will only be a +potential match by the kind-namespace-name criterion. + +For each resource the group correlation will be done by the next logic: 1. Exact match of apiVersion-kind-namespace-name -1. Exact Match in 3/4 fields from apiVersion, kind, namespace, name. (meaning - one of: kind-namespace-name or apiVersion-kind-name or - apiVersion-kind-namespace) -1. Exact Match in 2/4 fields from apiVersion, kind, namespace, name. (meaning - one of: kind-namespace or kind-name or apiVersion-kind) + 1. If there is a result in the reference, comparison will be done +1. Exact Match in 3/4 fields from apiVersion, kind, namespace, name. ( meaning exact match in: kind-namespace-name or + apiVersion-kind-name or apiVersion-kind-namespace) + 1. If there is a result in the reference, comparison will be done +1. Exact Match in 2/4 fields from apiVersion, kind, namespace, name. ( meaning exact match in: kind-namespace or + kind-name or apiVersion-kind) + 1. If there is a result in the reference, comparison will be done 1. Match kind + 1. If there is a result in the reference, comparison will be done 1. No match – comparison cannot be made and the file is flagged as unmatched. -In the event that a single CR matches multiple templates, the diff logic -assumes that the template with the smallest number of diffs from the given CR -is the intended template. This can be overridden by either specifying manual -matches (see the prior section), or be enabling strict match mode (see the -following section). - -###### Strict match mode - -Strict match correlation is based on which fields in the templates that are not -user-variable. In this mode, templates get matched to resources based on all -the features from the 4-tuple that are declared fixed (not user-variable) in -the templates. - -For example a template with a fixed namespace, kind, name and templated -(user-variable) apiVersion will only be a potential match by the -kind-namespace-name criterion, but would not match a CR with the same kind and -namespace if the name is different. - -To enable strict matching, add the following user config in your diff-config -(`-c` flag): - -```yaml -correlationSettings: - strictMatch: true -``` +We can phrase this logic in a more general form. Each CR will be correlated to a template with an exact match in the +largest number of fields from this group: apiVersion, kind, namespace, name. ### How it works diff --git a/pkg/compare/compare.go b/pkg/compare/compare.go index 38089a0b..8925478b 100644 --- a/pkg/compare/compare.go +++ b/pkg/compare/compare.go @@ -358,7 +358,7 @@ func (o *Options) setupCorrelators() error { correlators = append(correlators, manualCorrelator) } - groupCorrelator, err := NewGroupCorrelator(defaultFieldGroups, o.templates, o.userConfig.CorrelationSettings.StrictMatch) + groupCorrelator, err := NewGroupCorrelator(defaultFieldGroups, o.templates) if err != nil { return err } @@ -387,8 +387,7 @@ func (o *Options) setupOverrideCorrelators() error { correlators = append(correlators, manualOverrideCorrelator) } - // UserOverrides are never lenient; must match as exactly as possible - groupCorrelator, err := NewGroupCorrelator(defaultFieldGroups, o.userOverrides, true) + groupCorrelator, err := NewGroupCorrelator(defaultFieldGroups, o.userOverrides) if err != nil { return err } diff --git a/pkg/compare/compare_test.go b/pkg/compare/compare_test.go index 7105c086..64e95577 100644 --- a/pkg/compare/compare_test.go +++ b/pkg/compare/compare_test.go @@ -422,11 +422,6 @@ func TestCompareRun(t *testing.T) { defaultTest("SomeDiffs"). withVerboseOutput(). withChecks(defaultChecks.withPrefixedSuffix("withVebosityFlag")), - defaultTest("Strict Match"), - defaultTest("Strict Match"). - withUserConfig(userConfigFileName). - withSubTestSuffix("Strict Mode On"). - withChecks(defaultChecks.withPrefixedSuffix("strict_mode_on")), defaultTest("Invalid Resources Are Skipped"), defaultTest("Ref Contains Templates With Function Templates In Same File"), defaultTest("User Override"). diff --git a/pkg/compare/correlator.go b/pkg/compare/correlator.go index 24ff9620..035a91a4 100644 --- a/pkg/compare/correlator.go +++ b/pkg/compare/correlator.go @@ -115,12 +115,11 @@ type GroupCorrelator[T CorrelationEntry] struct { // For fieldsGroups = {{{"metadata", "namespace"}, {"kind"}}, {{"kind"}}} and the following templates: [fixedKindTemplate, fixedNamespaceKindTemplate] // the fixedNamespaceKindTemplate will be added to a mapping where the keys are in the format of `namespace_kind`. The fixedKindTemplate // will be added to a mapping where the keys are in the format of `kind`. -func NewGroupCorrelator[T CorrelationEntry](fieldGroups [][][]string, objects []T, strict bool) (*GroupCorrelator[T], error) { +func NewGroupCorrelator[T CorrelationEntry](fieldGroups [][][]string, objects []T) (*GroupCorrelator[T], error) { sort.Slice(fieldGroups, func(i, j int) bool { return len(fieldGroups[i]) >= len(fieldGroups[j]) }) core := GroupCorrelator[T]{} - first := true for _, group := range fieldGroups { fc := FieldCorrelator[T]{Fields: group, hashFunc: createGroupHashFunc(group)} newObjects := fc.ClaimTemplates(objects) @@ -130,9 +129,7 @@ func NewGroupCorrelator[T CorrelationEntry](fieldGroups [][][]string, objects [] continue } - // Only warn if we find duplicate templates matching on the most-specific FieldCorrelator, or in strict mode - fc.warnDupe = first || strict - first = false + objects = newObjects core.fieldCorrelators = append(core.fieldCorrelators, &fc) err := fc.ValidateTemplates() @@ -140,12 +137,8 @@ func NewGroupCorrelator[T CorrelationEntry](fieldGroups [][][]string, objects [] klog.Warning(err) } - if strict { - // In strict mode, only continue to process the objects that we haven't yet matched to a FieldCorrelator - objects = newObjects - if len(objects) == 0 { - break - } + if len(objects) == 0 { + break } } @@ -266,7 +259,6 @@ type FieldCorrelator[T CorrelationEntry] struct { Fields [][]string hashFunc templateHashFunc objects map[string][]T - warnDupe bool } func (f *FieldCorrelator[T]) ClaimTemplates(templates []T) []T { @@ -289,10 +281,6 @@ func (f *FieldCorrelator[T]) ClaimTemplates(templates []T) []T { } func (f *FieldCorrelator[T]) ValidateTemplates() error { - if !f.warnDupe { - return nil - } - errs := make([]error, 0) for _, values := range f.objects { if len(values) > 1 { diff --git a/pkg/compare/parsing.go b/pkg/compare/parsing.go index 0cabc07d..3dee9080 100644 --- a/pkg/compare/parsing.go +++ b/pkg/compare/parsing.go @@ -96,7 +96,6 @@ type UserConfig struct { type CorrelationSettings struct { ManualCorrelation ManualCorrelation `json:"manualCorrelation"` - StrictMatch bool `json:"strictMatch,omitempty"` } type ManualCorrelation struct { diff --git a/pkg/compare/testdata/StrictMatch/localerr.golden b/pkg/compare/testdata/StrictMatch/localerr.golden deleted file mode 100644 index 8053f59d..00000000 --- a/pkg/compare/testdata/StrictMatch/localerr.golden +++ /dev/null @@ -1,2 +0,0 @@ - -error code:1 diff --git a/pkg/compare/testdata/StrictMatch/localout.golden b/pkg/compare/testdata/StrictMatch/localout.golden deleted file mode 100644 index 55b4ad21..00000000 --- a/pkg/compare/testdata/StrictMatch/localout.golden +++ /dev/null @@ -1,44 +0,0 @@ -********************************** - -Cluster CR: apps/v1_Deployment_kubernetes-dashboard_different -Reference File: deploymentDashboard.yaml -Diff Output: diff -u -N TEMP/apps-v1_deployment_kubernetes-dashboard_different TEMP/apps-v1_deployment_kubernetes-dashboard_different ---- TEMP/apps-v1_deployment_kubernetes-dashboard_different DATE -+++ TEMP/apps-v1_deployment_kubernetes-dashboard_different DATE -@@ -3,7 +3,7 @@ - metadata: - labels: - k8s-app: kubernetes-dashboard -- name: kubernetes-dashboard -+ name: different - namespace: kubernetes-dashboard - spec: - replicas: 1 - -********************************** - -Cluster CR: apps/v1_Deployment_changed_changed -Reference File: deploymentMetrics.yaml -Diff Output: diff -u -N TEMP/apps-v1_deployment_changed_changed TEMP/apps-v1_deployment_changed_changed ---- TEMP/apps-v1_deployment_changed_changed DATE -+++ TEMP/apps-v1_deployment_changed_changed DATE -@@ -3,8 +3,8 @@ - metadata: - labels: - k8s-app: dashboard-metrics-scraper -- name: dashboard-metrics-scraper -- namespace: kubernetes-dashboard -+ name: changed -+ namespace: changed - spec: - replicas: 1 - revisionHistoryLimit: 10 - -********************************** - -Summary -CRs with diffs: 2/2 -No validation issues with the cluster -No CRs are unmatched to reference CRs -Metadata Hash: aa4c94f1307788e1da81f57718a9f1364d35d4ff6099fc633724bcf9d051a094 -No patched CRs diff --git a/pkg/compare/testdata/StrictMatch/localstrict_mode_onerr.golden b/pkg/compare/testdata/StrictMatch/localstrict_mode_onerr.golden deleted file mode 100644 index 8053f59d..00000000 --- a/pkg/compare/testdata/StrictMatch/localstrict_mode_onerr.golden +++ /dev/null @@ -1,2 +0,0 @@ - -error code:1 diff --git a/pkg/compare/testdata/StrictMatch/localstrict_mode_onout.golden b/pkg/compare/testdata/StrictMatch/localstrict_mode_onout.golden deleted file mode 100644 index 3f5626db..00000000 --- a/pkg/compare/testdata/StrictMatch/localstrict_mode_onout.golden +++ /dev/null @@ -1,11 +0,0 @@ -Summary -CRs with diffs: 0/0 -CRs in reference missing from the cluster: 2 -ExamplePart: - Dashboard: - Missing CRs: - - deploymentDashboard.yaml - - deploymentMetrics.yaml -No CRs are unmatched to reference CRs -Metadata Hash: aa4c94f1307788e1da81f57718a9f1364d35d4ff6099fc633724bcf9d051a094 -No patched CRs diff --git a/pkg/compare/testdata/StrictMatch/reference/deploymentDashboard.yaml b/pkg/compare/testdata/StrictMatch/reference/deploymentDashboard.yaml deleted file mode 100644 index 240407e1..00000000 --- a/pkg/compare/testdata/StrictMatch/reference/deploymentDashboard.yaml +++ /dev/null @@ -1,66 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: kubernetes-dashboard - namespace: kubernetes-dashboard -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: kubernetes-dashboard - template: - metadata: - labels: - k8s-app: kubernetes-dashboard - spec: - securityContext: - seccompProfile: - type: RuntimeDefault - containers: - - name: kubernetes-dashboard - image: kubernetesui/dashboard:v2.7.0 - imagePullPolicy: Always - ports: - - containerPort: 8443 - protocol: TCP - args: - - --auto-generate-certificates - - --namespace=kubernetes-dashboard - # Uncomment the following line to manually specify Kubernetes API server Host - # If not specified, Dashboard will attempt to auto discover the API server and connect - # to it. Uncomment only if the default does not work. - # - --apiserver-host=http://my-address:port - volumeMounts: - - name: kubernetes-dashboard-certs - mountPath: /certs - # Create on-disk volume to store exec logs - - mountPath: /tmp - name: tmp-volume - livenessProbe: - httpGet: - scheme: HTTPS - path: / - port: 8443 - initialDelaySeconds: 30 - timeoutSeconds: 30 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1001 - runAsGroup: 2001 - volumes: - - name: kubernetes-dashboard-certs - secret: - secretName: kubernetes-dashboard-certs - - name: tmp-volume - emptyDir: { } - serviceAccountName: kubernetes-dashboard - nodeSelector: - "kubernetes.io/os": linux - # Comment the following tolerations if Dashboard must not be deployed on master - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule diff --git a/pkg/compare/testdata/StrictMatch/reference/deploymentMetrics.yaml b/pkg/compare/testdata/StrictMatch/reference/deploymentMetrics.yaml deleted file mode 100644 index dd8da434..00000000 --- a/pkg/compare/testdata/StrictMatch/reference/deploymentMetrics.yaml +++ /dev/null @@ -1,19 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - labels: - k8s-app: dashboard-metrics-scraper - name: dashboard-metrics-scraper - namespace: kubernetes-dashboard -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: dashboard-metrics-scraper - template: - metadata: - labels: - k8s-app: dashboard-metrics-scraper - spec: -{{ if .spec.template.spec }}{{ .spec.template.spec | toYaml | indent 6 }}{{ end }} diff --git a/pkg/compare/testdata/StrictMatch/reference/metadata.yaml b/pkg/compare/testdata/StrictMatch/reference/metadata.yaml deleted file mode 100644 index 7e5042e4..00000000 --- a/pkg/compare/testdata/StrictMatch/reference/metadata.yaml +++ /dev/null @@ -1,8 +0,0 @@ -parts: - - name: ExamplePart - components: - - name: Dashboard - type: Required - requiredTemplates: - - path: deploymentDashboard.yaml - - path: deploymentMetrics.yaml diff --git a/pkg/compare/testdata/StrictMatch/resources/d2.yaml b/pkg/compare/testdata/StrictMatch/resources/d2.yaml deleted file mode 100644 index bbe722a8..00000000 --- a/pkg/compare/testdata/StrictMatch/resources/d2.yaml +++ /dev/null @@ -1,52 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - labels: - k8s-app: dashboard-metrics-scraper - name: changed - namespace: changed -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: dashboard-metrics-scraper - template: - metadata: - labels: - k8s-app: dashboard-metrics-scraper - spec: - securityContext: - seccompProfile: - type: RuntimeDefault - containers: - - name: dashboard-metrics-scraper - image: kubernetesui/metrics-scraper:v1.0.8 - ports: - - containerPort: 8000 - protocol: TCP - livenessProbe: - httpGet: - scheme: HTTP - path: / - port: 8000 - initialDelaySeconds: 30 - timeoutSeconds: 30 - volumeMounts: - - mountPath: /tmp - name: tmp-volume - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1001 - runAsGroup: 2001 - serviceAccountName: kubernetes-dashboard - nodeSelector: - "kubernetes.io/os": linux - # Comment the following tolerations if Dashboard must not be deployed on master - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule - volumes: - - name: tmp-volume - emptyDir: { } diff --git a/pkg/compare/testdata/StrictMatch/resources/deploymentDashboard.yaml b/pkg/compare/testdata/StrictMatch/resources/deploymentDashboard.yaml deleted file mode 100644 index fff02139..00000000 --- a/pkg/compare/testdata/StrictMatch/resources/deploymentDashboard.yaml +++ /dev/null @@ -1,66 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - labels: - k8s-app: kubernetes-dashboard - name: different - namespace: kubernetes-dashboard -spec: - replicas: 1 - revisionHistoryLimit: 10 - selector: - matchLabels: - k8s-app: kubernetes-dashboard - template: - metadata: - labels: - k8s-app: kubernetes-dashboard - spec: - securityContext: - seccompProfile: - type: RuntimeDefault - containers: - - name: kubernetes-dashboard - image: kubernetesui/dashboard:v2.7.0 - imagePullPolicy: Always - ports: - - containerPort: 8443 - protocol: TCP - args: - - --auto-generate-certificates - - --namespace=kubernetes-dashboard - # Uncomment the following line to manually specify Kubernetes API server Host - # If not specified, Dashboard will attempt to auto discover the API server and connect - # to it. Uncomment only if the default does not work. - # - --apiserver-host=http://my-address:port - volumeMounts: - - name: kubernetes-dashboard-certs - mountPath: /certs - # Create on-disk volume to store exec logs - - mountPath: /tmp - name: tmp-volume - livenessProbe: - httpGet: - scheme: HTTPS - path: / - port: 8443 - initialDelaySeconds: 30 - timeoutSeconds: 30 - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsUser: 1001 - runAsGroup: 2001 - volumes: - - name: kubernetes-dashboard-certs - secret: - secretName: kubernetes-dashboard-certs - - name: tmp-volume - emptyDir: { } - serviceAccountName: kubernetes-dashboard - nodeSelector: - "kubernetes.io/os": linux - # Comment the following tolerations if Dashboard must not be deployed on master - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule diff --git a/pkg/compare/testdata/StrictMatch/userconfig.yaml b/pkg/compare/testdata/StrictMatch/userconfig.yaml deleted file mode 100644 index d3c92e16..00000000 --- a/pkg/compare/testdata/StrictMatch/userconfig.yaml +++ /dev/null @@ -1,2 +0,0 @@ -correlationSettings: - strictMatch: true