codecatalyst-runner/pkg/workflows/replace_variable_handler.go (82 lines of code) (raw):
package workflows
import (
"context"
"fmt"
"regexp"
"slices"
"strings"
"github.com/aws/codecatalyst-runner-cli/command-runner/pkg/common"
"github.com/aws/codecatalyst-runner-cli/command-runner/pkg/runner"
"github.com/rs/zerolog/log"
)
// ReplaceVariableHandler converts the variables to outputs
func ReplaceVariableHandler(planOutputs map[string]map[string]string, secrets SecretProvider) runner.Feature {
replacer := &variableReplacer{
planOutputs: planOutputs,
secrets: secrets,
}
return func(ctx context.Context, plan runner.Plan, e runner.PlanExecutor) error {
log.Ctx(ctx).Debug().Msg("ENTER ReplaceVariableHandler")
for name, value := range plan.EnvironmentConfiguration().Env {
var newValue string
var err error
if newValue, err = replacer.Replace(ctx, plan, value); err != nil {
return err
}
plan.EnvironmentConfiguration().Env[name] = newValue
}
for _, cg := range plan.CommandGroups() {
for _, command := range cg.Commands {
for i, commandPart := range command {
if newValue, err := replacer.Replace(ctx, plan, commandPart); err != nil {
return err
} else {
command[i] = newValue
}
}
}
}
err := e(ctx)
log.Ctx(ctx).Debug().Msg("EXIT ReplaceVariableHandler")
return err
}
}
var replacementVariablePattern = regexp.MustCompile(`\${([a-zA-Z0-9\.\-_]+)\.([a-zA-Z0-9\-_]+)}`)
type variableReplacer struct {
planOutputs map[string]map[string]string
secrets SecretProvider
}
func (v *variableReplacer) Replace(ctx context.Context, plan runner.Plan, value string) (string, error) {
var rtnError error
return replacementVariablePattern.ReplaceAllStringFunc(value, func(match string) string {
varParts := replacementVariablePattern.FindStringSubmatch(match)
varPrefix := varParts[1]
varName := varParts[2]
if varPrefix == "Secrets" {
if plan.EnvironmentConfiguration().Env == nil {
plan.EnvironmentConfiguration().Env = make(map[string]string)
}
secretEnvName := fmt.Sprintf("CATALYST_SECRETS_%s", varName)
if secretValue, err := v.secrets.GetSecret(ctx, varName); err != nil {
log.Ctx(ctx).Err(err).Msg("unable to get secret")
rtnError = err
} else {
plan.EnvironmentConfiguration().Env[secretEnvName] = secretValue
}
log.Ctx(ctx).Debug().Msgf("Replacing SECRET %s with %s", varName, secretEnvName)
return fmt.Sprintf("${%s}", secretEnvName)
}
planId := strings.Replace(varPrefix, ".", "@", 1)
log.Ctx(ctx).Debug().Msgf("Adding DependsOn %s", planId)
if !slices.Contains(plan.DependsOn(), planId) {
plan.AddDependsOn(planId)
rtnError = common.ErrDefer
return value
}
newValue := v.planOutputs[planId][varName]
log.Ctx(ctx).Debug().Msgf("Replacing VAR %s with %s", match, newValue)
return newValue
}), rtnError
}
type SecretProvider interface {
GetSecret(ctx context.Context, name string) (string, error)
}