gitops-engine icon indicating copy to clipboard operation
gitops-engine copied to clipboard

Should remarshal LastAppliedConfigAnnotation before ThreeWayDiff

Open ciiiii opened this issue 3 years ago • 0 comments

Description

Config and live object are all handled with remarshal(), should object from LastAppliedConfigAnnotation need to be handled with remarshal() too? This can avoid null values in LastAppliedConfigAnnotation make diff result different.

Related Code

func Diff(config, live *unstructured.Unstructured, opts ...Option) (*DiffResult, error) {
	o := applyOptions(opts)
	if config != nil {
		config = remarshal(config, o)
		Normalize(config, opts...)
	}
	if live != nil {
		live = remarshal(live, o)
		Normalize(live, opts...)
	}
	orig, err := GetLastAppliedConfigAnnotation(live)
	if err != nil {
		o.log.V(1).Info(fmt.Sprintf("Failed to get last applied configuration: %v", err))
	} else {
		if orig != nil && config != nil {
                        orig = remarshal(orig, o) // add core here
			Normalize(orig, opts...)
			dr, err := ThreeWayDiff(orig, config, live)
			if err == nil {
				return dr, nil
			}
			o.log.V(1).Info(fmt.Sprintf("three-way diff calculation failed: %v. Falling back to two-way diff", err))
		}
	}
	return TwoWayDiff(config, live)
}

Example

live object:

apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"argocd.argoproj.io/instance":"test"},"name":"test","namespace":"default"},"spec":{"ports":[{"name":"http","nodePort":null,"port":8080,"targetPort":8080}],"selector":{"app":"test"},"type":"NodePort"}}
  creationTimestamp: "2022-01-17T10:34:45Z"
  labels:
    argocd.argoproj.io/instance: test
  name: test
  namespace: default
  resourceVersion: "199634708"
  selfLink: /api/v1/namespaces/default/services/test
  uid: 384f0d3f-5146-4654-a11b-e7d7065cab88
spec:
  clusterIP: 10.45.102.60
  externalTrafficPolicy: Cluster
  ports:
  - name: http
    nodePort: 30395
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: test
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

config object:

apiVersion: v1
kind: Service
metadata:
  labels:
    argocd.argoproj.io/instance: test
  name: test
  namespace: default
spec:
  ports:
  - name: http
    nodePort: null
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: test
  type: NodePort

When someone set nodePort of Service to null, k8s will allocate a port to the Service, so the live object will have nodePort: 30395 and config object will have nodePort: null, remarshal() will clear nodePort: null, so there will be no diff between live object and config object, but object from LastAppliedConfigAnnotation without remarshal() will broke the result, mark application OutOfSync, Maybe we can prevent this meaningless sync.

ciiiii avatar Jan 18 '22 07:01 ciiiii