From 7a1ebc7f6628743c1ce1ab6253b0f80c358d7a7d Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Mon, 9 Dec 2024 16:19:30 +0100 Subject: [PATCH] feat(controller): support "soaking" Freight Signed-off-by: Hidde Beydals --- internal/controller/stages/regular_stages.go | 23 +++++ .../controller/stages/regular_stages_test.go | 97 +++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/internal/controller/stages/regular_stages.go b/internal/controller/stages/regular_stages.go index 1213cae4d..743efc7ab 100644 --- a/internal/controller/stages/regular_stages.go +++ b/internal/controller/stages/regular_stages.go @@ -1694,6 +1694,29 @@ func (r *RegularStageReconciler) getPromotableFreight( ) } + // We have specific requirements for how long the Freight has been + // verified in the upstream Stage. Further filter the list of + // verified Freight based on the requirement. + if req.Sources.VerifiedFor != nil { + for _, verified := range verifiedFreight.Items { + // NB: If there is no verification timestamp for the Freight, + // then we cannot determine how long it has been verified in + // the upstream Stage. In this case, we skip the Freight + // from the list of promotable Freight. + if verifiedSince := verified.Status.VerifiedIn[upstream].VerifiedAt; verifiedSince != nil { + if time.Since(verifiedSince.Time) > req.Sources.VerifiedFor.Duration { + promotableFreight[originID] = append(promotableFreight[originID], verified) + } + } + } + + // Continue to the next Stage in the list of upstream Stages. + continue + } + + // We have no specific requirement for how long the Freight has + // been verified in the upstream Stage, so we can add all verified + // Freight to the promotable list. promotableFreight[originID] = append(promotableFreight[originID], verifiedFreight.Items...) } diff --git a/internal/controller/stages/regular_stages_test.go b/internal/controller/stages/regular_stages_test.go index 0d87a194b..83019d06a 100644 --- a/internal/controller/stages/regular_stages_test.go +++ b/internal/controller/stages/regular_stages_test.go @@ -4600,6 +4600,103 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { assert.Equal(t, "test-freight-1", promoList.Items[0].Spec.Freight) }, }, + { + name: "handles verified freight from upstream stages with verification duration requirement", + stage: &kargoapi.Stage{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "test-stage", + }, + Spec: kargoapi.StageSpec{ + RequestedFreight: []kargoapi.FreightRequest{ + { + Origin: kargoapi.FreightOrigin{ + Kind: kargoapi.FreightOriginKindWarehouse, + Name: "test-warehouse", + }, + Sources: kargoapi.FreightSources{ + Stages: []string{"upstream-stage"}, + VerifiedFor: &metav1.Duration{Duration: time.Hour}, + }, + }, + }, + }, + }, + objects: []client.Object{ + &kargoapi.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-project", + }, + Spec: &kargoapi.ProjectSpec{ + PromotionPolicies: []kargoapi.PromotionPolicy{ + { + Stage: "test-stage", + AutoPromotionEnabled: true, + }, + }, + }, + }, + &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "test-freight-1", + CreationTimestamp: metav1.Time{Time: now}, + }, + Status: kargoapi.FreightStatus{ + VerifiedIn: map[string]kargoapi.VerifiedStage{ + // Ignored because it does not have a timestamp. + "upstream-stage": {}, + }, + }, + }, + &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "test-freight-2", + CreationTimestamp: metav1.Time{Time: now.Add(-2 * time.Hour)}, + }, + Status: kargoapi.FreightStatus{ + VerifiedIn: map[string]kargoapi.VerifiedStage{ + "upstream-stage": { + // Should be selected because it was verified + // after the required duration. + VerifiedAt: &metav1.Time{Time: now.Add(-2 * time.Hour)}, + }, + }, + }, + }, + &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "test-freight-3", + CreationTimestamp: metav1.Time{Time: now.Add(-40 * time.Minute)}, + }, + Status: kargoapi.FreightStatus{ + VerifiedIn: map[string]kargoapi.VerifiedStage{ + "upstream-stage": { + // Should be ignored because it is too recent. + VerifiedAt: &metav1.Time{Time: now.Add(-39 * time.Minute)}, + }, + }, + }, + }, + }, + assertions: func( + t *testing.T, + _ *fakeevent.EventRecorder, + c client.Client, + _ kargoapi.StageStatus, + err error, + ) { + require.NoError(t, err) + + // Verify promotion was created + promoList := &kargoapi.PromotionList{} + require.NoError(t, c.List(context.Background(), promoList, client.InNamespace("fake-project"))) + require.Len(t, promoList.Items, 1) + assert.Equal(t, "test-freight-2", promoList.Items[0].Spec.Freight) + }, + }, { name: "handles freight approved for stage", stage: &kargoapi.Stage{