Generate label transformers for the prune selectors

This commit is contained in:
stefanprodan 2020-04-23 12:36:21 +03:00
parent d3e5c8faf9
commit b88626c0cc
6 changed files with 124 additions and 22 deletions

View File

@ -7,7 +7,7 @@
The kustomize-controller is a continuous delivery tool for Kubernetes, specialized in running
CD pipelines inside the cluster for workloads and infrastructure manifests
generated with Kustomize coming from source control systems.
coming from source control systems.
![overview](docs/diagrams/fluxcd-kustomize-source-controllers.png)

View File

@ -32,7 +32,7 @@ type KustomizationSpec struct {
// When enabled, the kustomization.yaml is automatically generated
// for all the Kubernetes manifests in the specified path and sub-directories.
// The generated kustomization.yaml will have common labels taken from the prune field.
// The generated kustomization.yaml contains a label transformer matching the prune field.
// +optional
Generate bool `json:"generate,omitempty"`

View File

@ -55,7 +55,7 @@ spec:
generate:
description: When enabled, the kustomization.yaml is automatically generated
for all the Kubernetes manifests in the specified path and sub-directories.
The generated kustomization.yaml will have common labels taken from
The generated kustomization.yaml contains a label transformer matching
the prune field.
type: boolean
healthChecks:

View File

@ -17,6 +17,8 @@ limitations under the License.
package controllers
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
@ -25,6 +27,7 @@ import (
"os/exec"
"path"
"strings"
"text/template"
"time"
"github.com/go-logr/logr"
@ -282,14 +285,8 @@ func (r *KustomizationReconciler) generate(kustomization kustomizev1.Kustomizati
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
labels := ""
if prune := kustomization.Spec.Prune; prune != "" {
labels = fmt.Sprintf("--labels %s", strings.ReplaceAll(prune, "=", ":"))
}
dirPath := path.Join(tmpDir, kustomization.Spec.Path)
cmd := fmt.Sprintf("cd %s && kustomize create --autodetect --recursive %s",
dirPath, labels)
cmd := fmt.Sprintf("cd %s && kustomize create --autodetect --recursive", dirPath)
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
output, err := command.CombinedOutput()
if err != nil {
@ -298,6 +295,86 @@ func (r *KustomizationReconciler) generate(kustomization kustomizev1.Kustomizati
}
return fmt.Errorf("kustomize create failed: %s", string(output))
}
if err := r.generateLabelTransformer(kustomization, dirPath); err != nil {
return err
}
return nil
}
func (r *KustomizationReconciler) generateLabelTransformer(kustomization kustomizev1.Kustomization, dirPath string) error {
labelTransformer := `
apiVersion: builtin
kind: LabelTransformer
metadata:
name: labels
labels:
{{- range $key, $value := . }}
{{ $key }}: {{ $value }}
{{- end }}
fieldSpecs:
- path: metadata/labels
create: true
`
transformers := `
transformers:
- gc-labels.yaml
`
prune := kustomization.Spec.Prune
if prune == "" {
return nil
}
// transform prune into label selectors
selectors := make(map[string]string)
for _, ls := range strings.Split(prune, ",") {
if kv := strings.Split(ls, "="); len(kv) == 2 {
selectors[kv[0]] = kv[1]
}
}
t, err := template.New("tmpl").Parse(labelTransformer)
if err != nil {
return fmt.Errorf("labelTransformer template parsing failed: %w", err)
}
var data bytes.Buffer
writer := bufio.NewWriter(&data)
if err := t.Execute(writer, selectors); err != nil {
return fmt.Errorf("labelTransformer template excution failed: %w", err)
}
if err := writer.Flush(); err != nil {
return fmt.Errorf("labelTransformer flush failed: %w", err)
}
file, err := os.Create(path.Join(dirPath, "gc-labels.yaml"))
if err != nil {
return fmt.Errorf("labelTransformer create failed: %w", err)
}
defer file.Close()
if _, err := file.WriteString(data.String()); err != nil {
return fmt.Errorf("labelTransformer write failed: %w", err)
}
if err := file.Sync(); err != nil {
return fmt.Errorf("labelTransformer sync failed: %w", err)
}
kfile, err := os.OpenFile(path.Join(dirPath, "kustomization.yaml"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("kustomization file open failed: %w", err)
}
defer kfile.Close()
if _, err := kfile.WriteString(transformers); err != nil {
return fmt.Errorf("kustomization file append failed: %w", err)
}
return nil
}

View File

@ -20,7 +20,6 @@ import (
"context"
"fmt"
"os/exec"
"strings"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/event"
@ -76,12 +75,12 @@ func (gc KustomizationGarbageCollectPredicate) Delete(e event.DeleteEvent) bool
command := exec.CommandContext(ctx, "/bin/sh", "-c", cmd)
if output, err := command.CombinedOutput(); err != nil {
gc.Log.Error(err, "Garbage collection failed",
"output", string(output),
strings.ToLower(k.Kind), fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()),
"output", string(output))
} else {
gc.Log.Info("Garbage collection completed",
"output", string(output),
strings.ToLower(k.Kind), fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()),
"output", string(output))
}
}
}

View File

@ -18,7 +18,7 @@ type KustomizationSpec struct {
// When enabled, the kustomization.yaml is automatically generated
// for all the Kubernetes manifests in the specified path and sub-directories.
// The generated kustomization.yaml will have common labels taken from the prune field.
// The generated kustomization.yaml contains a label transformer matching the prune field.
// +optional
Generate bool `json:"generate,omitempty"`
@ -119,16 +119,18 @@ Source supported types:
> **Note** that the source should contain the kustomization.yaml and all the
> Kubernetes manifests and configuration files referenced in the kustomization.yaml.
> If your repository contains only plain Kubernetes then you can enable kustomization.yaml generation.
> If your repository contains only plain manifests, then you should enable kustomization.yaml generation.
## Generate kustomization.yaml
If your repository contains plain Kubernetes manifests, you can configure the
controller to generate a `kustomization.yaml` by setting `spec.generate` to `true`.
When `spec.generate` is enabled, the `kustomization.yaml` is automatically generated for
When `spec.generate` is enabled, the `kustomization.yaml` file is automatically generated for
all the Kubernetes manifests in the `spec.path` and sub-directories.
The generated `kustomization.yaml` will have common labels taken from the `spec.prune` field.
If the `spec.prune` is not empty, the controller generates a label transformer to enable
[garbage collection](#garbage-collection).
## Reconciliation
@ -162,8 +164,8 @@ but are missing from the current apply, are removed from cluster automatically.
Garbage collection is also performed when a Kustomization object is deleted,
triggering a removal of all Kubernetes objects previously applied on the cluster.
Tpo enable garbage collection, all Kubernetes objects must have a common label that matches the `spec.prune`
[label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
Tpo enable garbage collection, all Kubernetes objects must have common labels matching the `spec.prune`
[label selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
For example, `prune: env=dev,app=frontend` requires a `kustomization.yaml` with `commonLabels`:
@ -175,9 +177,33 @@ commonLabels:
app: frontend
```
> **Note** that each kustomization must have a unique combination of labels values, otherwise the
> **Note** that each kustomization must have a unique combination of label key/values, otherwise the
> garbage collection will remove resources outside of the kustomization scope.
Another option to label all Kubernetes objects, is with label transformers:
```yaml
apiVersion: builtin
kind: LabelTransformer
metadata:
name: labels
labels:
env: dev
app: frontend
fieldSpecs:
- path: metadata/labels
create: true
```
Save the above file as `labels.yaml` and add it to your `kustomization.yaml`:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
transformers:
- labels.yaml
```
## Health assessment
A kustomization can contain a series of health checks used to determine the