Implement pruning based on snapshots

This commit is contained in:
stefanprodan 2020-05-05 16:56:27 +03:00
parent dbe3e69508
commit f96f3e326f
4 changed files with 101 additions and 33 deletions

View File

@ -60,6 +60,9 @@ const (
// ApplyFailedReason represents the fact that the kustomization apply failed.
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 string = "ArtifactFailed"

View File

@ -163,6 +163,20 @@ func KustomizationNotReady(kustomization Kustomization, reason, message string)
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
func (in *Kustomization) GetTimeout() time.Duration {
duration := in.Spec.Interval.Duration

View File

@ -248,11 +248,22 @@ func (r *KustomizationReconciler) sync(
), err
}
// health assessment
err = r.checkHealth(kustomization)
// prune
err = r.prune(kustomization, snapshot)
if err != nil {
return kustomizev1.KustomizationNotReady(
kustomization,
kustomizev1.PruneFailedReason,
err.Error(),
), err
}
// health assessment
err = r.checkHealth(kustomization)
if err != nil {
return kustomizev1.KustomizationNotReadySnapshot(
kustomization,
snapshot,
kustomizev1.HealthCheckFailedReason,
"health check failed",
), err
@ -364,7 +375,7 @@ func (r *KustomizationReconciler) generateLabelTransformer(kustomization kustomi
}{
Name: kustomization.GetName(),
},
Labels: gcLabels(kustomization.GetName(), revision),
Labels: gcLabels(kustomization.GetName(), kustomization.GetNamespace(), revision),
FieldSpecs: []kustypes.FieldSpec{
{Path: "metadata/labels", CreateIfNotPresent: true},
},
@ -476,6 +487,25 @@ func (r *KustomizationReconciler) apply(kustomization kustomizev1.Kustomization,
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 {
timeout := kustomization.GetTimeout() + (time.Second * 1)
ctx, cancel := context.WithTimeout(context.Background(), timeout)

View File

@ -68,41 +68,62 @@ type KustomizationGarbageCollectPredicate struct {
func (gc KustomizationGarbageCollectPredicate) Delete(e event.DeleteEvent) bool {
if k, ok := e.Object.(*kustomizev1.Kustomization); ok {
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",
"kustomization", fmt.Sprintf("%s/%s", k.GetNamespace(), k.GetName()))
for ns, kinds := range k.Status.Snapshot.NamespacedKinds() {
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)
}
}
prune(k.GetTimeout(), k.GetName(), k.GetNamespace(), k.Status.Snapshot, gc.Log)
}
}
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) {
ctx, cancel := context.WithTimeout(context.Background(), timeout+time.Second)
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{
"kustomization/name": name,
"kustomization/name": fmt.Sprintf("%s-%s", name, namespace),
"kustomization/revision": checksum(revision),
}
}
func gcSelectors(name, revision string) string {
return fmt.Sprintf("kustomization/name=%s,kustomization/revision=%s", name, checksum(revision))
func gcSelectors(name, namespace, revision string) string {
return fmt.Sprintf("kustomization/name=%s-%s,kustomization/revision=%s", name, namespace, checksum(revision))
}
func checksum(in string) string {