From e6d3458faec9e2a806fa2876eb39053a1d4d1c83 Mon Sep 17 00:00:00 2001 From: Hidde Beydals Date: Tue, 17 Sep 2024 15:58:03 +0200 Subject: [PATCH] feat: wire directives into promotion reconciler Signed-off-by: Hidde Beydals --- api/v1alpha1/promotion_types.go | 14 ++++++ internal/controller/promotions/promotions.go | 50 ++++++++++++++++---- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/api/v1alpha1/promotion_types.go b/api/v1alpha1/promotion_types.go index 69ae3fe07..54ef216de 100644 --- a/api/v1alpha1/promotion_types.go +++ b/api/v1alpha1/promotion_types.go @@ -3,6 +3,7 @@ package v1alpha1 import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" ) type PromotionPhase string @@ -100,6 +101,19 @@ type PromotionStep struct { Config *apiextensionsv1.JSON `json:"config,omitempty" protobuf:"bytes,3,opt,name=config"` } +// GetConfig returns the Config field as unmarshalled YAML. +func (s *PromotionStep) GetConfig() map[string]any { + if s.Config == nil { + return nil + } + + var config map[string]any + if err := yaml.Unmarshal(s.Config.Raw, &config); err != nil { + return nil + } + return config +} + // PromotionStatus describes the current state of the transition represented by // a Promotion. type PromotionStatus struct { diff --git a/internal/controller/promotions/promotions.go b/internal/controller/promotions/promotions.go index b79ccb201..01e83736a 100644 --- a/internal/controller/promotions/promotions.go +++ b/internal/controller/promotions/promotions.go @@ -26,6 +26,7 @@ import ( "github.com/akuity/kargo/internal/controller/promotion" "github.com/akuity/kargo/internal/controller/runtime" "github.com/akuity/kargo/internal/credentials" + "github.com/akuity/kargo/internal/directives" "github.com/akuity/kargo/internal/kargo" "github.com/akuity/kargo/internal/kubeclient" libEvent "github.com/akuity/kargo/internal/kubernetes/event" @@ -53,8 +54,9 @@ func ReconcilerConfigFromEnv() ReconcilerConfig { // reconciler reconciles Promotion resources. type reconciler struct { - kargoClient client.Client - promoMechanisms promotion.Mechanism + kargoClient client.Client + directivesEngine *directives.Engine + promoMechanisms promotion.Mechanism cfg ReconcilerConfig @@ -186,9 +188,15 @@ func newReconciler( } r := &reconciler{ kargoClient: kargoClient, - recorder: recorder, - cfg: cfg, - pqs: &pqs, + directivesEngine: directives.NewEngine( + directives.BuiltinsRegistry(), + credentialsDB, + kargoClient, + argocdClient, + ), + recorder: recorder, + cfg: cfg, + pqs: &pqs, promoMechanisms: promotion.NewMechanisms( kargoClient, argocdClient, @@ -497,11 +505,35 @@ func (r *reconciler) promote( stage, ) - if err := r.promoMechanisms.Promote(ctx, stage, workingPromo); err != nil { - return nil, err - } + if workingPromo.Spec.Steps == nil { + // If the Promotion has no steps, assume we are dealing with "legacy" + // Promotion mechanisms. + if err := r.promoMechanisms.Promote(ctx, stage, workingPromo); err != nil { + return nil, err + } - logger.Debug("promotion", "phase", workingPromo.Status.Phase) + logger.Debug("promotion", "phase", workingPromo.Status.Phase) + } else { + // If the Promotion has steps, execute them in sequence. + var steps []directives.Step + for _, step := range workingPromo.Spec.Steps { + steps = append(steps, directives.Step{ + Directive: step.Step, + Alias: step.As, + Config: step.GetConfig(), + }) + } + status, err := r.directivesEngine.Execute(ctx, steps) + switch status { + case directives.StatusSuccess: + workingPromo.Status.Phase = kargoapi.PromotionPhaseSucceeded + case directives.StatusFailure: + workingPromo.Status.Phase = kargoapi.PromotionPhaseFailed + } + if err != nil { + return nil, err + } + } if workingPromo.Status.Phase == kargoapi.PromotionPhaseSucceeded { // Trigger re-verification of the Stage if the promotion succeeded and