Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the GC checksum from labels to annotations #362

Merged
merged 1 commit into from
Jun 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions controllers/kustomization_controller_gc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand All @@ -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 {
Expand Down
86 changes: 86 additions & 0 deletions controllers/kustomization_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
})
})
})
})

Expand Down
23 changes: 20 additions & 3 deletions controllers/kustomization_gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
stefanprodan marked this conversation as resolved.
Show resolved Hide resolved
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 {
Expand All @@ -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,
}
}

Expand All @@ -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
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been lost, please restore it for the annotions

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"`
Expand Down Expand Up @@ -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
}