Implement pruning based on snapshots
This commit is contained in:
parent
dbe3e69508
commit
f96f3e326f
|
@ -60,6 +60,9 @@ const (
|
||||||
// ApplyFailedReason represents the fact that the kustomization apply failed.
|
// ApplyFailedReason represents the fact that the kustomization apply failed.
|
||||||
ApplyFailedReason string = "ApplyFailed"
|
ApplyFailedReason string = "ApplyFailed"
|
||||||
|
|
||||||
|
// PruneFailedReason represents the fact that the kustomization pruning failed.
|
||||||
|
PruneFailedReason string = "PruneFailed"
|
||||||
|
|
||||||
// ArtifactFailedReason represents the fact that the artifact download failed.
|
// ArtifactFailedReason represents the fact that the artifact download failed.
|
||||||
ArtifactFailedReason string = "ArtifactFailed"
|
ArtifactFailedReason string = "ArtifactFailed"
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,20 @@ func KustomizationNotReady(kustomization Kustomization, reason, message string)
|
||||||
return kustomization
|
return kustomization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func KustomizationNotReadySnapshot(kustomization Kustomization, snapshot *Snapshot, reason, message string) Kustomization {
|
||||||
|
kustomization.Status.Conditions = []Condition{
|
||||||
|
{
|
||||||
|
Type: ReadyCondition,
|
||||||
|
Status: corev1.ConditionFalse,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
Reason: reason,
|
||||||
|
Message: message,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
kustomization.Status.Snapshot = snapshot
|
||||||
|
return kustomization
|
||||||
|
}
|
||||||
|
|
||||||
// GetTimeout returns the timeout with default
|
// GetTimeout returns the timeout with default
|
||||||
func (in *Kustomization) GetTimeout() time.Duration {
|
func (in *Kustomization) GetTimeout() time.Duration {
|
||||||
duration := in.Spec.Interval.Duration
|
duration := in.Spec.Interval.Duration
|
||||||
|
|
|
@ -248,11 +248,22 @@ func (r *KustomizationReconciler) sync(
|
||||||
), err
|
), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// health assessment
|
// prune
|
||||||
err = r.checkHealth(kustomization)
|
err = r.prune(kustomization, snapshot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kustomizev1.KustomizationNotReady(
|
return kustomizev1.KustomizationNotReady(
|
||||||
kustomization,
|
kustomization,
|
||||||
|
kustomizev1.PruneFailedReason,
|
||||||
|
err.Error(),
|
||||||
|
), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// health assessment
|
||||||
|
err = r.checkHealth(kustomization)
|
||||||
|
if err != nil {
|
||||||
|
return kustomizev1.KustomizationNotReadySnapshot(
|
||||||
|
kustomization,
|
||||||
|
snapshot,
|
||||||
kustomizev1.HealthCheckFailedReason,
|
kustomizev1.HealthCheckFailedReason,
|
||||||
"health check failed",
|
"health check failed",
|
||||||
), err
|
), err
|
||||||
|
@ -364,7 +375,7 @@ func (r *KustomizationReconciler) generateLabelTransformer(kustomization kustomi
|
||||||
}{
|
}{
|
||||||
Name: kustomization.GetName(),
|
Name: kustomization.GetName(),
|
||||||
},
|
},
|
||||||
Labels: gcLabels(kustomization.GetName(), revision),
|
Labels: gcLabels(kustomization.GetName(), kustomization.GetNamespace(), revision),
|
||||||
FieldSpecs: []kustypes.FieldSpec{
|
FieldSpecs: []kustypes.FieldSpec{
|
||||||
{Path: "metadata/labels", CreateIfNotPresent: true},
|
{Path: "metadata/labels", CreateIfNotPresent: true},
|
||||||
},
|
},
|
||||||
|
@ -476,6 +487,25 @@ func (r *KustomizationReconciler) apply(kustomization kustomizev1.Kustomization,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *KustomizationReconciler) prune(kustomization kustomizev1.Kustomization, snapshot *kustomizev1.Snapshot) error {
|
||||||
|
if kustomization.Status.Snapshot == nil || snapshot == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if kustomization.Status.Snapshot.Revision == snapshot.Revision {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !prune(kustomization.GetTimeout(),
|
||||||
|
kustomization.GetName(),
|
||||||
|
kustomization.GetNamespace(),
|
||||||
|
kustomization.Status.Snapshot,
|
||||||
|
r.Log,
|
||||||
|
) {
|
||||||
|
return fmt.Errorf("pruning failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *KustomizationReconciler) checkHealth(kustomization kustomizev1.Kustomization) error {
|
func (r *KustomizationReconciler) checkHealth(kustomization kustomizev1.Kustomization) error {
|
||||||
timeout := kustomization.GetTimeout() + (time.Second * 1)
|
timeout := kustomization.GetTimeout() + (time.Second * 1)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
|
|
@ -68,41 +68,62 @@ type KustomizationGarbageCollectPredicate struct {
|
||||||
func (gc KustomizationGarbageCollectPredicate) Delete(e event.DeleteEvent) bool {
|
func (gc KustomizationGarbageCollectPredicate) Delete(e event.DeleteEvent) bool {
|
||||||
if k, ok := e.Object.(*kustomizev1.Kustomization); ok {
|
if k, ok := e.Object.(*kustomizev1.Kustomization); ok {
|
||||||
if k.Spec.Prune != "" && !k.Spec.Suspend && k.Status.Snapshot != nil {
|
if k.Spec.Prune != "" && !k.Spec.Suspend && k.Status.Snapshot != nil {
|
||||||
timeout := k.GetTimeout()
|
|
||||||
selector := gcSelectors(k.GetName(), k.Status.Snapshot.Revision)
|
|
||||||
gc.Log.Info("Garbage collection started",
|
gc.Log.Info("Garbage collection started",
|
||||||
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
|
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
|
||||||
|
|
||||||
for ns, kinds := range k.Status.Snapshot.NamespacedKinds() {
|
prune(k.GetTimeout(), k.GetName(), k.GetNamespace(), k.Status.Snapshot, gc.Log)
|
||||||
for _, kind := range kinds {
|
|
||||||
if output, err := deleteByKind(timeout, kind, ns, selector); err != nil {
|
|
||||||
gc.Log.Error(err, "Garbage collection failed for namespaced objects",
|
|
||||||
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
|
|
||||||
} else {
|
|
||||||
gc.Log.Info("Garbage collection for namespaced objects completed",
|
|
||||||
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()),
|
|
||||||
"output", output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, kind := range k.Status.Snapshot.NonNamespacedKinds() {
|
|
||||||
if output, err := deleteByKind(timeout, kind, "", selector); err != nil {
|
|
||||||
gc.Log.Error(err, "Garbage collection failed for non-namespaced objects",
|
|
||||||
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
|
|
||||||
} else {
|
|
||||||
gc.Log.Info("Garbage collection for non-namespaced objects completed",
|
|
||||||
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()),
|
|
||||||
"output", output)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prune(timeout time.Duration, name string, namespace string, snapshot *kustomizev1.Snapshot, log logr.Logger) bool {
|
||||||
|
selector := gcSelectors(name, namespace, snapshot.Revision)
|
||||||
|
ok := true
|
||||||
|
outInfo := ""
|
||||||
|
outErr := ""
|
||||||
|
for ns, kinds := range snapshot.NamespacedKinds() {
|
||||||
|
for _, kind := range kinds {
|
||||||
|
if output, err := deleteByKind(timeout, kind, ns, selector); err != nil {
|
||||||
|
outErr += " " + err.Error()
|
||||||
|
ok = false
|
||||||
|
} else {
|
||||||
|
outInfo += " " + output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if outErr == "" {
|
||||||
|
log.Info("Garbage collection for namespaced objects completed",
|
||||||
|
"kustomization", fmt.Sprintf("%s/%s", namespace, name),
|
||||||
|
"output", outInfo)
|
||||||
|
} else {
|
||||||
|
log.Error(fmt.Errorf(outErr), "Garbage collection for namespaced objects failed",
|
||||||
|
"kustomization", fmt.Sprintf("%s/%s", namespace, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
outInfo = ""
|
||||||
|
outErr = ""
|
||||||
|
for _, kind := range snapshot.NonNamespacedKinds() {
|
||||||
|
if output, err := deleteByKind(timeout, kind, "", selector); err != nil {
|
||||||
|
outErr += " " + err.Error()
|
||||||
|
ok = false
|
||||||
|
} else {
|
||||||
|
outInfo += " " + output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if outErr == "" {
|
||||||
|
log.Info("Garbage collection for non-namespaced objects completed",
|
||||||
|
"kustomization", fmt.Sprintf("%s/%s", namespace, name),
|
||||||
|
"output", outInfo)
|
||||||
|
} else {
|
||||||
|
log.Error(fmt.Errorf(outErr), "Garbage collection for non-namespaced objects failed",
|
||||||
|
"kustomization", fmt.Sprintf("%s/%s", namespace, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func deleteByKind(timeout time.Duration, kind, namespace, selector string) (string, error) {
|
func deleteByKind(timeout time.Duration, kind, namespace, selector string) (string, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout+time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout+time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -120,15 +141,15 @@ func deleteByKind(timeout time.Duration, kind, namespace, selector string) (stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func gcLabels(name, revision string) map[string]string {
|
func gcLabels(name, namespace, revision string) map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"kustomization/name": name,
|
"kustomization/name": fmt.Sprintf("%s-%s", name, namespace),
|
||||||
"kustomization/revision": checksum(revision),
|
"kustomization/revision": checksum(revision),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func gcSelectors(name, revision string) string {
|
func gcSelectors(name, namespace, revision string) string {
|
||||||
return fmt.Sprintf("kustomization/name=%s,kustomization/revision=%s", name, checksum(revision))
|
return fmt.Sprintf("kustomization/name=%s-%s,kustomization/revision=%s", name, namespace, checksum(revision))
|
||||||
}
|
}
|
||||||
|
|
||||||
func checksum(in string) string {
|
func checksum(in string) string {
|
||||||
|
|
Loading…
Reference in New Issue