Skip to content

Commit

Permalink
chore(directives): implement step-level vars
Browse files Browse the repository at this point in the history
Signed-off-by: Hidde Beydals <[email protected]>
  • Loading branch information
hiddeco committed Dec 16, 2024
1 parent ed6ca0a commit 63db956
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 195 deletions.
2 changes: 1 addition & 1 deletion internal/controller/promotions/promotions.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ func (r *reconciler) promote(
Kind: step.Uses,
Alias: step.As,
Retry: step.Retry,
Inputs: step.Inputs,
Vars: step.Vars,

Check warning on line 474 in internal/controller/promotions/promotions.go

View check run for this annotation

Codecov / codecov/patch

internal/controller/promotions/promotions.go#L474

Added line #L474 was not covered by tests
Config: step.Config.Raw,
}
}
Expand Down
33 changes: 15 additions & 18 deletions internal/directives/promotions.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ type PromotionStep struct {
Alias string
// Retry is the retry configuration for the PromotionStep.
Retry *kargoapi.PromotionStepRetry
// Inputs is a list of inputs to be made available to the Config of this step.
Inputs []kargoapi.PromotionStepInput
// Vars is a list of variables definitions that can be used by the
// PromotionStep.
Vars []kargoapi.PromotionVariable
// Config is an opaque JSON to be passed to the PromotionStepRunner executing
// this step.
Config []byte
Expand Down Expand Up @@ -146,8 +147,6 @@ func (s *PromotionStep) GetConfig(
return nil, err
}

inputs := s.GetInputs()

evaledCfgJSON, err := expressions.EvaluateJSONTemplate(
s.Config,
map[string]any{
Expand All @@ -157,7 +156,6 @@ func (s *PromotionStep) GetConfig(
"stage": promoCtx.Stage,
},
"vars": vars,
"inputs": inputs,
"secrets": promoCtx.Secrets,
"outputs": state,
},
Expand Down Expand Up @@ -196,10 +194,18 @@ func (s *PromotionStep) GetConfig(
// GetVars returns the variables defined in the PromotionStep. The variables are
// evaluated in the context of the provided PromotionContext.
func (s *PromotionStep) GetVars(promoCtx PromotionContext) (map[string]any, error) {
vars := make(map[string]any, len(promoCtx.Vars))
var rawVars = make(map[string]string, len(promoCtx.Vars))
for _, v := range promoCtx.Vars {
rawVars[v.Name] = v.Value
}
for _, v := range s.Vars {
rawVars[v.Name] = v.Value
}

Check warning on line 203 in internal/directives/promotions.go

View check run for this annotation

Codecov / codecov/patch

internal/directives/promotions.go#L202-L203

Added lines #L202 - L203 were not covered by tests

vars := make(map[string]any, len(rawVars))
for k, v := range rawVars {
newVar, err := expressions.EvaluateTemplate(
v.Value,
v,
map[string]any{
"ctx": map[string]any{
"project": promoCtx.Project,
Expand All @@ -210,22 +216,13 @@ func (s *PromotionStep) GetVars(promoCtx PromotionContext) (map[string]any, erro
},
)
if err != nil {
return nil, fmt.Errorf("error pre-processing promotion variable %q: %w", v.Name, err)
return nil, fmt.Errorf("error pre-processing promotion variable %q: %w", k, err)

Check warning on line 219 in internal/directives/promotions.go

View check run for this annotation

Codecov / codecov/patch

internal/directives/promotions.go#L219

Added line #L219 was not covered by tests
}
vars[v.Name] = newVar
vars[k] = newVar
}
return vars, nil
}

// GetInputs returns the inputs of the PromotionStep as a map.
func (s *PromotionStep) GetInputs() map[string]any {
inputs := make(map[string]any, len(s.Inputs))
for _, i := range s.Inputs {
inputs[i.Name] = i.Value
}
return inputs
}

// PromotionResult is the result of a user-defined promotion process executed by
// the Engine. It aggregates the status and output of the individual
// PromotionStepResults returned by the PromotionStepRunner executing each
Expand Down
76 changes: 42 additions & 34 deletions internal/kargo/promotion_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ import (
"strings"

"github.com/oklog/ulid/v2"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"

kargoapi "github.com/akuity/kargo/api/v1alpha1"
"github.com/akuity/kargo/internal/api/user"
Expand Down Expand Up @@ -103,7 +101,13 @@ func (b *PromotionBuilder) buildSteps(ctx context.Context, stage kargoapi.Stage)
switch {
case step.Task != nil:
alias := step.GetAlias(i)
taskSteps, err := b.inflateTaskSteps(ctx, stage.Namespace, alias, step)
taskSteps, err := b.inflateTaskSteps(
ctx,
stage.Namespace,
alias,
stage.Spec.PromotionTemplate.Spec.Vars,
step,
)
if err != nil {
return nil, fmt.Errorf("inflate tasks steps for task %q (%q): %w", step.Task.Name, alias, err)
}
Expand All @@ -121,14 +125,15 @@ func (b *PromotionBuilder) buildSteps(ctx context.Context, stage kargoapi.Stage)
func (b *PromotionBuilder) inflateTaskSteps(
ctx context.Context,
project, taskAlias string,
promoVars []kargoapi.PromotionVariable,
taskStep kargoapi.PromotionStep,
) ([]kargoapi.PromotionStep, error) {
task, err := b.getTaskSpec(ctx, project, taskStep.Task)
if err != nil {
return nil, err
}

inputs, err := promotionTaskInputsToStepInputs(task.Inputs, taskStep.Config)
vars, err := promotionTaskVarsToStepVars(task.Vars, promoVars, taskStep.Vars)
if err != nil {
return nil, err
}
Expand All @@ -142,9 +147,9 @@ func (b *PromotionBuilder) inflateTaskSteps(
// the Promotion.
step.As = generatePromotionTaskStepAlias(taskAlias, step.GetAlias(i))

// With the inputs validated and mapped, they are now available to
// With the variables validated and mapped, they are now available to
// the Config of the step during the Promotion execution.
step.Inputs = inputs
step.Vars = vars

// Append the inflated step to the list of steps.
steps = append(steps, *step)
Expand Down Expand Up @@ -215,42 +220,45 @@ func generatePromotionTaskStepAlias(taskAlias, stepAlias string) string {
return fmt.Sprintf("%s%s%s", taskAlias, aliasSeparator, stepAlias)
}

// promotionTaskInputsToStepInputs validates the task step config against the task
// inputs, and maps the config to inputs for the inflated steps.
func promotionTaskInputsToStepInputs(
taskInputs []kargoapi.PromotionTaskInput,
stepConfig *apiextensionsv1.JSON,
) ([]kargoapi.PromotionStepInput, error) {
if len(taskInputs) == 0 {
// promotionTaskVarsToStepVars validates the presence of the PromotionTask
// variables and maps them to variables which can be used by the inflated
// PromotionStep.
func promotionTaskVarsToStepVars(
taskVars, promoVars, stepVars []kargoapi.PromotionVariable,
) ([]kargoapi.PromotionVariable, error) {
if len(taskVars) == 0 {
return nil, nil
}

if stepConfig == nil {
return nil, errors.New("missing step config")
promoVarsMap := make(map[string]kargoapi.PromotionVariable, len(promoVars))
for _, v := range promoVars {
promoVarsMap[v.Name] = v
}

config := make(map[string]any, len(taskInputs))
if err := yaml.Unmarshal(stepConfig.Raw, &config); err != nil {
return nil, fmt.Errorf("unmarshal step config: %w", err)
stepVarsMap := make(map[string]kargoapi.PromotionVariable, len(stepVars))
for _, v := range stepVars {
stepVarsMap[v.Name] = v
}

inputs := make([]kargoapi.PromotionStepInput, 0, len(taskInputs))
for _, input := range taskInputs {
iv := input.Default
if cv, exists := config[input.Name]; exists {
strVal, ok := cv.(string)
if !ok {
return nil, fmt.Errorf("input %q must be a string", input.Name)
}
iv = strVal
vars := make([]kargoapi.PromotionVariable, 0, len(taskVars))
for _, v := range taskVars {
if stepVar, ok := stepVarsMap[v.Name]; ok && stepVar.Value != "" {
vars = append(vars, stepVar)
continue
}
if iv == "" {
return nil, fmt.Errorf("missing required input %q", input.Name)

if promoVar, ok := promoVarsMap[v.Name]; ok && promoVar.Value != "" {
// If the variable is defined in the Promotion, the engine will
// automatically use the value from the Promotion, and we do not
// have to explicitly set it here.
continue
}
inputs = append(inputs, kargoapi.PromotionStepInput{
Name: input.Name,
Value: iv,
})

if v.Value == "" {
return nil, fmt.Errorf("missing value for variable %q", v.Name)
}

vars = append(vars, v)
}
return inputs, nil
return vars, nil
}
Loading

0 comments on commit 63db956

Please sign in to comment.