Skip to content

Commit

Permalink
Merge pull request #362 from JaneLiuL/main
Browse files Browse the repository at this point in the history
Move the GC checksum from labels to annotations
  • Loading branch information
stefanprodan authored Jun 14, 2021
2 parents 30dd0c3 + 6979dba commit 2838997
Showing 5 changed files with 175 additions and 21 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ Prerequisites:
* go >= 1.13
* kubebuilder >= 2.3
* kustomize >= 3.1
* kubectl >= 1.21

You can run the unit tests by simply doing

4 changes: 2 additions & 2 deletions controllers/kustomization_controller_gc_test.go
Original file line number Diff line number Diff line change
@@ -144,7 +144,7 @@ data:

var configMap corev1.ConfigMap
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: "first", Namespace: namespace.Name}, &configMap)).To(Succeed())
Expect(configMap.Labels[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]).NotTo(BeEmpty())
Expect(configMap.Annotations[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]).NotTo(BeEmpty())

manifest.Body = configMapManifest("second")
artifact, err = artifactServer.ArtifactFromFiles([]testserver.File{manifest})
@@ -163,7 +163,7 @@ data:
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "first", Namespace: namespace.Name}, &configMap)
Expect(apierrors.IsNotFound(err)).To(BeTrue())
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: "second", Namespace: namespace.Name}, &configMap)).To(Succeed())
Expect(configMap.Labels[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]).NotTo(BeEmpty())
Expect(configMap.Annotations[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]).NotTo(BeEmpty())

Expect(k8sClient.Delete(context.Background(), kustomization)).To(Succeed())
Eventually(func() bool {
86 changes: 86 additions & 0 deletions controllers/kustomization_controller_test.go
Original file line number Diff line number Diff line change
@@ -360,6 +360,92 @@ spec:
Expect(deployment.Spec.Selector.MatchLabels["app"]).To(Equal("v2"))
})
})

Describe("Kustomization resource annotation", func() {
deploymentManifest := func(namespace string) string {
return fmt.Sprintf(`---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
namespace: %s
spec:
selector:
matchLabels:
app: test-deployment
template:
metadata:
labels:
app: test-deployment
spec:
containers:
- name: test
image: podinfo
`,
namespace)
}

It("should have no annotation when if prune is false", func() {
manifests := []testserver.File{
{
Name: "deployment.yaml",
Body: deploymentManifest(namespace.Name),
},
}
artifact, err := httpServer.ArtifactFromFiles(manifests)
Expect(err).NotTo(HaveOccurred())

url := fmt.Sprintf("%s/%s", httpServer.URL(), artifact)

repositoryName := types.NamespacedName{
Name: fmt.Sprintf("%s", randStringRunes(5)),
Namespace: namespace.Name,
}
repository := readyGitRepository(repositoryName, url, "v1", "")
Expect(k8sClient.Create(context.Background(), repository)).To(Succeed())
Expect(k8sClient.Status().Update(context.Background(), repository)).To(Succeed())
defer k8sClient.Delete(context.Background(), repository)

kName := types.NamespacedName{
Name: fmt.Sprintf("%s", randStringRunes(5)),
Namespace: namespace.Name,
}
k := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kName.Name,
Namespace: kName.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
KubeConfig: kubeconfig,
Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./",
Prune: false,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourcev1.GitRepositoryKind,
Name: repository.Name,
},
Suspend: false,
Timeout: &metav1.Duration{Duration: 60 * time.Second},
Validation: "client",
Force: true,
},
}
Expect(k8sClient.Create(context.Background(), k)).To(Succeed())
defer k8sClient.Delete(context.Background(), k)

got := &kustomizev1.Kustomization{}
Eventually(func() bool {
_ = k8sClient.Get(context.Background(), kName, got)
c := apimeta.FindStatusCondition(got.Status.Conditions, meta.ReadyCondition)
return c != nil && c.Reason == meta.ReconciliationSucceededReason
}, timeout, interval).Should(BeTrue())
Expect(got.Status.LastAppliedRevision).To(Equal("v1"))

deployment := &appsv1.Deployment{}
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: "test-deployment", Namespace: namespace.Name}, deployment)).To(Succeed())
Expect(deployment.Annotations[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]).To(BeEmpty())
})
})
})
})

23 changes: 20 additions & 3 deletions controllers/kustomization_gc.go
Original file line number Diff line number Diff line change
@@ -137,9 +137,21 @@ func (kgc *KustomizeGarbageCollector) Prune(timeout time.Duration, name string,
return changeSet, true
}

// Check both labels and annotations for the checksum to preserve backwards compatibility
func (kgc *KustomizeGarbageCollector) isStale(obj unstructured.Unstructured) bool {
itemChecksum := obj.GetLabels()[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]
return kgc.newChecksum == "" || itemChecksum != kgc.newChecksum
itemLabelChecksum := obj.GetLabels()[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]
itemAnnotationChecksum := obj.GetAnnotations()[fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group)]

switch kgc.newChecksum {
case "":
return true
case itemAnnotationChecksum:
return false
case itemLabelChecksum:
return false
default:
return true
}
}

func (kgc *KustomizeGarbageCollector) shouldSkip(obj unstructured.Unstructured) bool {
@@ -156,7 +168,11 @@ func gcLabels(name, namespace, checksum string) map[string]string {
return map[string]string{
fmt.Sprintf("%s/name", kustomizev1.GroupVersion.Group): name,
fmt.Sprintf("%s/namespace", kustomizev1.GroupVersion.Group): namespace,
fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group): checksum,
}
}
func gcAnnotation(checksum string) map[string]string {
return map[string]string{
fmt.Sprintf("%s/checksum", kustomizev1.GroupVersion.Group): checksum,
}
}

@@ -165,4 +181,5 @@ func selectorLabels(name, namespace string) map[string]string {
fmt.Sprintf("%s/name", kustomizev1.GroupVersion.Group): name,
fmt.Sprintf("%s/namespace", kustomizev1.GroupVersion.Group): namespace,
}

}
82 changes: 66 additions & 16 deletions controllers/kustomization_generator.go
Original file line number Diff line number Diff line change
@@ -36,13 +36,13 @@ import (
kustypes "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"

"github.com/fluxcd/pkg/apis/kustomize"

kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
"github.com/fluxcd/pkg/apis/kustomize"
)

const (
transformerFileName = "kustomization-gc-labels.yaml"
transformerFileName = "kustomization-gc-labels.yaml"
transformerAnnotationFileName = "kustomization-gc-annotations.yaml"
)

type KustomizeGenerator struct {
@@ -68,6 +68,9 @@ func (kg *KustomizeGenerator) WriteFile(ctx context.Context, dirPath string) (st
if err := kg.generateLabelTransformer(checksum, dirPath); err != nil {
return "", err
}
if err = kg.generateAnnotationTransformer(checksum, dirPath); err != nil {
return "", err
}

data, err := ioutil.ReadFile(kfile)
if err != nil {
@@ -86,18 +89,16 @@ func (kg *KustomizeGenerator) WriteFile(ctx context.Context, dirPath string) (st
}

if len(kus.Transformers) == 0 {
kus.Transformers = []string{transformerFileName}
kus.Transformers = []string{transformerFileName, transformerAnnotationFileName}
} else {
var exists bool
for _, transformer := range kus.Transformers {
if transformer == transformerFileName {
exists = true
break
}
}
if !exists {
if !find(kus.Transformers, transformerFileName) {
kus.Transformers = append(kus.Transformers, transformerFileName)
}

if !find(kus.Transformers, transformerAnnotationFileName) {
kus.Transformers = append(kus.Transformers, transformerAnnotationFileName)
}

}

if kg.kustomization.Spec.TargetNamespace != "" {
@@ -277,13 +278,52 @@ func (kg *KustomizeGenerator) checksum(ctx context.Context, dirPath string) (str
return fmt.Sprintf("%x", sha1.Sum(resources)), nil
}

func (kg *KustomizeGenerator) generateAnnotationTransformer(checksum, dirPath string) error {
var annotations map[string]string
// add checksum annotations only if GC is enabled
if kg.kustomization.Spec.Prune {
annotations = gcAnnotation(checksum)
}

var lt = struct {
ApiVersion string `json:"apiVersion" yaml:"apiVersion"`
Kind string `json:"kind" yaml:"kind"`
Metadata struct {
Name string `json:"name" yaml:"name"`
} `json:"metadata" yaml:"metadata"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
FieldSpecs []kustypes.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}{
ApiVersion: "builtin",
Kind: "AnnotationsTransformer",
Metadata: struct {
Name string `json:"name" yaml:"name"`
}{
Name: kg.kustomization.GetName(),
},
Annotations: annotations,
FieldSpecs: []kustypes.FieldSpec{
{Path: "metadata/annotations", CreateIfNotPresent: true},
},
}

data, err := yaml.Marshal(lt)
if err != nil {
return err
}

annotationsFile := filepath.Join(dirPath, transformerAnnotationFileName)
if err := ioutil.WriteFile(annotationsFile, data, os.ModePerm); err != nil {
return err
}

return nil
}

func (kg *KustomizeGenerator) generateLabelTransformer(checksum, dirPath string) error {
labels := selectorLabels(kg.kustomization.GetName(), kg.kustomization.GetNamespace())

// add checksum label only if GC is enabled
if kg.kustomization.Spec.Prune {
labels = gcLabels(kg.kustomization.GetName(), kg.kustomization.GetNamespace(), checksum)
}
labels = gcLabels(kg.kustomization.GetName(), kg.kustomization.GetNamespace(), checksum)

var lt = struct {
ApiVersion string `json:"apiVersion" yaml:"apiVersion"`
@@ -358,3 +398,13 @@ func buildKustomization(fs filesys.FileSystem, dirPath string) (resmap.ResMap, e
k := krusty.MakeKustomizer(buildOptions)
return k.Run(fs, dirPath)
}

func find(source []string, value string) bool {
for _, item := range source {
if item == value {
return true
}
}

return false
}

0 comments on commit 2838997

Please sign in to comment.