Generate label transformers for the prune selectors
This commit is contained in:
parent
d3e5c8faf9
commit
b88626c0cc
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue