Skip to content

Commit 06f6ab6

Browse files
committed
Implement reconciliation history tracking
Signed-off-by: Stefan Prodan <[email protected]>
1 parent dc4fa59 commit 06f6ab6

File tree

3 files changed

+56
-7
lines changed

3 files changed

+56
-7
lines changed

internal/controller/kustomization_controller.go

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727

2828
securejoin "github.com/cyphar/filepath-securejoin"
2929
celtypes "github.com/google/cel-go/common/types"
30+
"github.com/opencontainers/go-digest"
3031
corev1 "k8s.io/api/core/v1"
3132
apierrors "k8s.io/apimachinery/pkg/api/errors"
3233
apimeta "k8s.io/apimachinery/pkg/api/meta"
@@ -281,6 +282,7 @@ func (r *KustomizationReconciler) reconcile(
281282
src sourcev1.Source,
282283
patcher *patch.SerialPatcher,
283284
statusReaders []func(apimeta.RESTMapper) engine.StatusReader) error {
285+
reconcileStart := time.Now()
284286
log := ctrl.LoggerFrom(ctx)
285287

286288
// Update status with the reconciliation progress.
@@ -314,13 +316,14 @@ func (r *KustomizationReconciler) reconcile(
314316
}(tmpDir)
315317

316318
// Download artifact and extract files to the tmp dir.
317-
if err = fetch.NewArchiveFetcherWithLogger(
318-
r.artifactFetchRetries,
319-
tar.UnlimitedUntarSize,
320-
tar.UnlimitedUntarSize,
321-
os.Getenv("SOURCE_CONTROLLER_LOCALHOST"),
322-
ctrl.LoggerFrom(ctx),
323-
).Fetch(src.GetArtifact().URL, src.GetArtifact().Digest, tmpDir); err != nil {
319+
fetcher := fetch.New(
320+
fetch.WithLogger(ctrl.LoggerFrom(ctx)),
321+
fetch.WithRetries(r.artifactFetchRetries),
322+
fetch.WithMaxDownloadSize(tar.UnlimitedUntarSize),
323+
fetch.WithUntar(tar.WithMaxUntarSize(tar.UnlimitedUntarSize)),
324+
fetch.WithHostnameOverwrite(os.Getenv("SOURCE_CONTROLLER_LOCALHOST")),
325+
)
326+
if err = fetcher.Fetch(src.GetArtifact().URL, src.GetArtifact().Digest, tmpDir); err != nil {
324327
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", err)
325328
return err
326329
}
@@ -398,6 +401,13 @@ func (r *KustomizationReconciler) reconcile(
398401
return err
399402
}
400403

404+
// Calculate the digest of the built resources for history tracking.
405+
checksum := digest.FromBytes(resources).String()
406+
historyMeta := map[string]string{"revision": revision}
407+
if originRevision != "" {
408+
historyMeta["originRevision"] = originRevision
409+
}
410+
401411
// Convert the build result into Kubernetes unstructured objects.
402412
objects, err := ssautil.ReadObjects(bytes.NewReader(resources))
403413
if err != nil {
@@ -423,6 +433,7 @@ func (r *KustomizationReconciler) reconcile(
423433
// Validate and apply resources in stages.
424434
drifted, changeSet, err := r.apply(ctx, resourceManager, obj, revision, originRevision, objects)
425435
if err != nil {
436+
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta)
426437
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
427438
return err
428439
}
@@ -431,6 +442,7 @@ func (r *KustomizationReconciler) reconcile(
431442
newInventory := inventory.New()
432443
err = inventory.AddChangeSet(newInventory, changeSet)
433444
if err != nil {
445+
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta)
434446
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
435447
return err
436448
}
@@ -441,12 +453,14 @@ func (r *KustomizationReconciler) reconcile(
441453
// Detect stale resources which are subject to garbage collection.
442454
staleObjects, err := inventory.Diff(oldInventory, newInventory)
443455
if err != nil {
456+
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta)
444457
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
445458
return err
446459
}
447460

448461
// Run garbage collection for stale resources that do not have pruning disabled.
449462
if _, err := r.prune(ctx, resourceManager, obj, revision, originRevision, staleObjects); err != nil {
463+
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.PruneFailedReason, historyMeta)
450464
conditions.MarkFalse(obj, meta.ReadyCondition, meta.PruneFailedReason, "%s", err)
451465
return err
452466
}
@@ -462,6 +476,7 @@ func (r *KustomizationReconciler) reconcile(
462476
isNewRevision,
463477
drifted,
464478
changeSet.ToObjMetadataSet()); err != nil {
479+
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.HealthCheckFailedReason, historyMeta)
465480
conditions.MarkFalse(obj, meta.ReadyCondition, meta.HealthCheckFailedReason, "%s", err)
466481
return err
467482
}
@@ -475,6 +490,11 @@ func (r *KustomizationReconciler) reconcile(
475490
meta.ReadyCondition,
476491
meta.ReconciliationSucceededReason,
477492
"Applied revision: %s", revision)
493+
obj.Status.History.Upsert(checksum,
494+
time.Now(),
495+
time.Since(reconcileStart),
496+
meta.ReconciliationSucceededReason,
497+
historyMeta)
478498

479499
return nil
480500
}

internal/controller/kustomization_origin_revision_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ stringData:
115115

116116
g.Expect(resultK.Status.LastAppliedOriginRevision).To(Equal("orev"))
117117

118+
g.Expect(resultK.Status.History).To(HaveLen(1))
119+
g.Expect(resultK.Status.History[0].Digest).ToNot(BeEmpty())
120+
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
121+
g.Expect(resultK.Status.History[0].FirstReconciled.Time).ToNot(BeZero())
122+
g.Expect(resultK.Status.History[0].LastReconciled.Time).ToNot(BeZero())
123+
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
124+
g.Expect(resultK.Status.History[0].LastReconciledDuration.Duration).To(BeNumerically(">", 0))
125+
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision, "orev"))
126+
118127
events := getEvents(kustomizationKey.Name, nil)
119128
g.Expect(events).To(Not(BeEmpty()))
120129

internal/controller/kustomization_wait_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ parameters:
136136
g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation))
137137

138138
kstatusCheck.CheckErr(ctx, resultK)
139+
140+
g.Expect(resultK.Status.History).To(HaveLen(1))
141+
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
142+
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
143+
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
139144
})
140145

141146
t.Run("reports progressing status", func(t *testing.T) {
@@ -192,6 +197,11 @@ parameters:
192197
g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation - 1))
193198

194199
kstatusCheck.CheckErr(ctx, resultK)
200+
201+
g.Expect(resultK.Status.History).To(HaveLen(2))
202+
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
203+
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.HealthCheckFailedReason))
204+
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
195205
})
196206

197207
t.Run("emits unhealthy event", func(t *testing.T) {
@@ -228,6 +238,11 @@ parameters:
228238
g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation))
229239

230240
kstatusCheck.CheckErr(ctx, resultK)
241+
242+
g.Expect(resultK.Status.History).To(HaveLen(2))
243+
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(2))
244+
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
245+
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
231246
})
232247

233248
t.Run("emits recovery event", func(t *testing.T) {
@@ -258,6 +273,11 @@ parameters:
258273
g.Expect(resultK.Status.LastAttemptedRevision).To(BeIdenticalTo(resultK.Status.LastAppliedRevision))
259274

260275
kstatusCheck.CheckErr(ctx, resultK)
276+
277+
g.Expect(resultK.Status.History).To(HaveLen(3))
278+
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
279+
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
280+
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
261281
})
262282

263283
t.Run("emits event for the new revision", func(t *testing.T) {

0 commit comments

Comments
 (0)