Implement garbage collection
- add GarbageCollect predicate - remove the Kubernetes objects previously applied on the cluster, based on the prune label select - add garbage collection spec to docs
This commit is contained in:
parent
c1cb216262
commit
eee47333cf
39
README.md
39
README.md
|
|
@ -1,5 +1,7 @@
|
||||||
# kustomize-controller
|
# kustomize-controller
|
||||||
|
|
||||||
|
[](https://github.com/fluxcd/kustomize-controller/actions)
|
||||||
|
|
||||||
The kustomize-controller is a Kubernetes operator that applies kustomizations in-cluster.
|
The kustomize-controller is a Kubernetes operator that applies kustomizations in-cluster.
|
||||||
|
|
||||||

|

|
||||||
|
|
@ -19,7 +21,7 @@ A kustomization object defines the source of Kubernetes manifests by referencing
|
||||||
the path to the kustomization file,
|
the path to the kustomization file,
|
||||||
and a label selector used for garbage collection of resources removed from the Git source.
|
and a label selector used for garbage collection of resources removed from the Git source.
|
||||||
|
|
||||||
Specification:
|
### Specification
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// KustomizationSpec defines the desired state of a kustomization.
|
// KustomizationSpec defines the desired state of a kustomization.
|
||||||
|
|
@ -29,7 +31,7 @@ type KustomizationSpec struct {
|
||||||
// +required
|
// +required
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
|
||||||
// Label selector used for prune operations, e.g. env=staging.
|
// Label selector used for garbage collection.
|
||||||
// +kubebuilder:validation:Pattern="^.*=.*$"
|
// +kubebuilder:validation:Pattern="^.*=.*$"
|
||||||
// +optional
|
// +optional
|
||||||
Prune string `json:"prune,omitempty"`
|
Prune string `json:"prune,omitempty"`
|
||||||
|
|
@ -44,9 +46,27 @@ type KustomizationSpec struct {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Supported source kinds:
|
### Supported source kinds
|
||||||
|
|
||||||
* [GitRepository](https://github.com/fluxcd/source-controller/blob/master/docs/spec/v1alpha1/gitrepositories.md)
|
* [GitRepository](https://github.com/fluxcd/source-controller/blob/master/docs/spec/v1alpha1/gitrepositories.md)
|
||||||
|
|
||||||
|
### Garbage collection
|
||||||
|
|
||||||
|
Garbage collection means that the Kubernetes objects that were previously applied on the cluster
|
||||||
|
but are missing from the current apply, will be removed. Garbage collection is also performed when a Kustomization
|
||||||
|
object is deleted, triggering a removal of all Kubernetes objects previously applied on the cluster.
|
||||||
|
|
||||||
|
When garbage collection is enabled, all Kubernetes objects must have a common label that matches the `prune`
|
||||||
|
[label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
|
||||||
|
|
||||||
|
For example, `prune: env=dev` requires a `kustomization.yaml` with `commonLabels`:
|
||||||
|
```yaml
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
commonLabels:
|
||||||
|
env: dev
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Build prerequisites:
|
Build prerequisites:
|
||||||
|
|
@ -74,6 +94,8 @@ make docker-build docker-push dev-deploy IMG=your-docker-hub-username/kustomize-
|
||||||
|
|
||||||
### Define a Git repository source
|
### Define a Git repository source
|
||||||
|
|
||||||
|
Create a source object that points to a Git repository containing Kubernetes and Kustomize manifests:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: source.fluxcd.io/v1alpha1
|
apiVersion: source.fluxcd.io/v1alpha1
|
||||||
kind: GitRepository
|
kind: GitRepository
|
||||||
|
|
@ -87,6 +109,9 @@ spec:
|
||||||
branch: master
|
branch: master
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For private repositories, SSH or token based authentication can be
|
||||||
|
[configured with Kubernetes secrets](https://github.com/fluxcd/source-controller/blob/master/docs/spec/v1alpha1/gitrepositories.md).
|
||||||
|
|
||||||
Save the above file and apply it on the cluster.
|
Save the above file and apply it on the cluster.
|
||||||
You can wait for the source controller to assemble an artifact from the head of the repo master branch with:
|
You can wait for the source controller to assemble an artifact from the head of the repo master branch with:
|
||||||
|
|
||||||
|
|
@ -118,9 +143,9 @@ spec:
|
||||||
name: podinfo
|
name: podinfo
|
||||||
```
|
```
|
||||||
|
|
||||||
With `spec.path` we tell the controller where to look for the kustomization file and with `spec.prune` we
|
With `spec.path` we tell the controller where to look for the `kustomization.yaml` file.
|
||||||
configure garbage collection. With `spec.interval` we tell the controller how often it should reconcile
|
With `spec.prune` we configure garbage collection.
|
||||||
the cluster state.
|
With `spec.interval` we tell the controller how often it should reconcile the cluster state.
|
||||||
|
|
||||||
Save the above file and apply it on the cluster.
|
Save the above file and apply it on the cluster.
|
||||||
You can wait for the kustomize controller to apply the manifest corresponding to the dev overlay with:
|
You can wait for the kustomize controller to apply the manifest corresponding to the dev overlay with:
|
||||||
|
|
@ -201,7 +226,7 @@ spec:
|
||||||
Based on the above definition, the kustomize controller will build and apply a kustomization that matches the semver range
|
Based on the above definition, the kustomize controller will build and apply a kustomization that matches the semver range
|
||||||
set in the Git repository manifest.
|
set in the Git repository manifest.
|
||||||
|
|
||||||
## GitOps pipeline
|
## GitOps workflow
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
* create a `GitRepository` per app (example repo [podinfo-deploy](https://github.com/stefanprodan/podinfo-deploy))
|
* create a `GitRepository` per app (example repo [podinfo-deploy](https://github.com/stefanprodan/podinfo-deploy))
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ type KustomizationSpec struct {
|
||||||
// +required
|
// +required
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
|
||||||
// Label selector used for prune operations, e.g. env=staging.
|
// Label selector used for garbage collection.
|
||||||
// +kubebuilder:validation:Pattern="^.*=.*$"
|
// +kubebuilder:validation:Pattern="^.*=.*$"
|
||||||
// +optional
|
// +optional
|
||||||
Prune string `json:"prune,omitempty"`
|
Prune string `json:"prune,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ spec:
|
||||||
pattern: ^\./
|
pattern: ^\./
|
||||||
type: string
|
type: string
|
||||||
prune:
|
prune:
|
||||||
description: Label selector used for prune operations, e.g. env=staging.
|
description: Label selector used for garbage collection.
|
||||||
pattern: ^.*=.*$
|
pattern: ^.*=.*$
|
||||||
type: string
|
type: string
|
||||||
sourceRef:
|
sourceRef:
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func (r *KustomizationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro
|
||||||
// try git sync
|
// try git sync
|
||||||
syncedKustomization, err := r.sync(ctx, *kustomization.DeepCopy(), source)
|
syncedKustomization, err := r.sync(ctx, *kustomization.DeepCopy(), source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Kustomization sync failed")
|
log.Error(err, "Kustomization apply failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// update status
|
// update status
|
||||||
|
|
@ -101,6 +101,7 @@ func (r *KustomizationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro
|
||||||
func (r *KustomizationReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *KustomizationReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
return ctrl.NewControllerManagedBy(mgr).
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
For(&kustomizev1.Kustomization{}).
|
For(&kustomizev1.Kustomization{}).
|
||||||
|
WithEventFilter(KustomizationGarbageCollectPredicate{Log: r.Log}).
|
||||||
WithEventFilter(KustomizationSyncAtPredicate{}).
|
WithEventFilter(KustomizationSyncAtPredicate{}).
|
||||||
Complete(r)
|
Complete(r)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ limitations under the License.
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||||
|
|
||||||
|
|
@ -50,3 +54,30 @@ func (KustomizationSyncAtPredicate) Update(e event.UpdateEvent) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KustomizationGarbageCollectPredicate struct {
|
||||||
|
predicate.Funcs
|
||||||
|
Log logr.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes all Kubernetes objects based on the prune label selector.
|
||||||
|
func (gc KustomizationGarbageCollectPredicate) Delete(e event.DeleteEvent) bool {
|
||||||
|
if k, ok := e.Object.(*kustomizev1.Kustomization); ok {
|
||||||
|
if k.Spec.Prune != "" {
|
||||||
|
cmd := fmt.Sprintf("kubectl delete all --all-namespaces --timeout=%s -l %s",
|
||||||
|
k.Spec.Interval.Duration.String(), k.Spec.Prune)
|
||||||
|
command := exec.Command("/bin/sh", "-c", cmd)
|
||||||
|
if output, err := command.CombinedOutput(); err != nil {
|
||||||
|
gc.Log.Error(err, "Garbage collection failed",
|
||||||
|
"output", string(output),
|
||||||
|
"Kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
|
||||||
|
} else {
|
||||||
|
gc.Log.Info("Garbage collection completed",
|
||||||
|
"output", string(output),
|
||||||
|
"Kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue