Skip to content

Commit

Permalink
WIP Dry Run
Browse files Browse the repository at this point in the history
Some refactoring, call prepareKubeConfig only once per RG
  • Loading branch information
janboll committed Nov 22, 2024
1 parent 6f408ea commit 031d7b2
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 43 deletions.
45 changes: 45 additions & 0 deletions tooling/templatize/pkg/config/types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package config

import "testing"

func TestGetByPath(t *testing.T) {
tests := []struct {
name string
vars Variables
path string
want any
found bool
}{
{
name: "simple",
vars: Variables{
"key": "value",
},
path: "key",
want: "value",
found: true,
},
{
name: "nested",
vars: Variables{
"key": Variables{
"key": "value",
},
},
path: "key.key",
want: "value",
found: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, found := tt.vars.GetByPath(tt.path)
if got != tt.want {
t.Errorf("Variables.GetByPath() got = %v, want %v", got, tt.want)
}
if found != tt.found {
t.Errorf("Variables.GetByPath() found = %v, want %v", found, tt.found)
}
})
}
}
45 changes: 43 additions & 2 deletions tooling/templatize/pkg/pipeline/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ func (rg *resourceGroup) run(ctx context.Context, options *PipelineRunOptions) e
}

logger := logr.FromContextOrDiscard(ctx)

var kubeconfigFile string
if !options.DryRun {
kubeconfigFile, err := prepareKubeConfig(ctx, executionTarget)
if kubeconfigFile != "" {
defer func() {
if err := os.Remove(kubeconfigFile); err != nil {
logger.V(5).Error(err, "failed to delete kubeconfig file", "kubeconfig", kubeconfigFile)
}
}()
}
if err != nil {
return fmt.Errorf("failed to prepare kubeconfig: %w", err)
}
}

for _, step := range rg.Steps {
if options.Step != "" && step.Name != options.Step {
// skip steps that don't match the specified step name
Expand All @@ -109,6 +125,7 @@ func (rg *resourceGroup) run(ctx context.Context, options *PipelineRunOptions) e
"aksCluster", executionTarget.AKSClusterName,
),
),
kubeconfigFile,
executionTarget, options,
)
if err != nil {
Expand All @@ -118,20 +135,44 @@ func (rg *resourceGroup) run(ctx context.Context, options *PipelineRunOptions) e
return nil
}

func (s *step) run(ctx context.Context, executionTarget *ExecutionTarget, options *PipelineRunOptions) error {
func (s *step) run(ctx context.Context, kubeconfigFile string, executionTarget *ExecutionTarget, options *PipelineRunOptions) error {
fmt.Println("\n---------------------")
if options.DryRun {
fmt.Println("This is a dry run!")
}
fmt.Println(s.description())
fmt.Print("\n")

switch s.Action {
case "Shell":
return s.runShellStep(ctx, executionTarget, options)
return s.runShellStep(ctx, kubeconfigFile, options)
case "ARM":
return s.runArmStep(ctx, executionTarget, options)
default:
return fmt.Errorf("unsupported action type %q", s.Action)
}
}

func prepareKubeConfig(ctx context.Context, executionTarget *ExecutionTarget) (string, error) {
logger := logr.FromContextOrDiscard(ctx)
kubeconfigFile := ""
// prepare kubeconfig
if executionTarget.AKSClusterName != "" {
logger.V(5).Info("Building kubeconfig for AKS cluster")
kubeconfigFile, err := executionTarget.KubeConfig(ctx)
if err != nil {
return "", fmt.Errorf("failed to build kubeconfig for %s: %w", executionTarget.aksID(), err)
}
defer func() {
if err := os.Remove(kubeconfigFile); err != nil {
logger.V(5).Error(err, "failed to delete kubeconfig file", "kubeconfig", kubeconfigFile)
}
}()
logger.V(5).Info("kubeconfig set to shell execution environment", "kubeconfig", kubeconfigFile)
}
return kubeconfigFile, nil
}

func (s *step) description() string {
var details []string
switch s.Action {
Expand Down
62 changes: 28 additions & 34 deletions tooling/templatize/pkg/pipeline/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package pipeline
import (
"context"
"fmt"
"os"
"os/exec"

"github.com/go-logr/logr"
Expand All @@ -12,7 +11,26 @@ import (
"github.com/Azure/ARO-HCP/tooling/templatize/pkg/utils"
)

func (s *step) runShellStep(ctx context.Context, executionTarget *ExecutionTarget, options *PipelineRunOptions) error {
func (s *step) createCommand(ctx context.Context, dryRun bool, envVars map[string]string) (*exec.Cmd, error) {
var cmd *exec.Cmd
if dryRun {
if s.DryRun.Command == nil && s.DryRun.EnvVars == nil {
return nil, fmt.Errorf("dry-run is enabled but no dry-run command or env vars are defined")
}
for _, e := range s.DryRun.EnvVars {
envVars[e.Name] = e.Value
}
if s.DryRun.Command != nil {
cmd = exec.CommandContext(ctx, s.DryRun.Command[0], s.DryRun.Command[1:]...)
}
} else {
cmd = exec.CommandContext(ctx, s.Command[0], s.Command[1:]...)
}
cmd.Env = append(cmd.Env, utils.MapToEnvVarArray(envVars)...)
return cmd, nil
}

func (s *step) runShellStep(ctx context.Context, kubeconfigFile string, options *PipelineRunOptions) error {
logger := logr.FromContextOrDiscard(ctx)

// build ENV vars
Expand All @@ -21,28 +39,17 @@ func (s *step) runShellStep(ctx context.Context, executionTarget *ExecutionTarge
return fmt.Errorf("failed to build env vars: %w", err)
}

// prepare kubeconfig
if executionTarget.AKSClusterName != "" {
logger.V(5).Info("Building kubeconfig for AKS cluster")
kubeconfigFile, err := executionTarget.KubeConfig(ctx)
if err != nil {
return fmt.Errorf("failed to build kubeconfig for %s: %w", executionTarget.aksID(), err)
}
defer func() {
if err := os.Remove(kubeconfigFile); err != nil {
logger.V(5).Error(err, "failed to delete kubeconfig file", "kubeconfig", kubeconfigFile)
}
}()
envVars["KUBECONFIG"] = kubeconfigFile
logger.V(5).Info("kubeconfig set to shell execution environment", "kubeconfig", kubeconfigFile)
// execute the command
cmd, err := s.createCommand(ctx, options.DryRun, envVars)
if err != nil {
return fmt.Errorf("failed to create shell command: %w", err)
}

// TODO handle dry-run
if kubeconfigFile != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("KUBECONFIG=%s", kubeconfigFile))
}

// execute the command
logger.V(5).Info(fmt.Sprintf("Executing shell command: %s\n", s.Command), "command", s.Command)
cmd := exec.CommandContext(ctx, s.Command[0], s.Command[1:]...)
cmd.Env = append(cmd.Env, utils.MapToEnvVarArray(envVars)...)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to execute shell command: %s %w", string(output), err)
Expand All @@ -67,20 +74,7 @@ func (s *step) getEnvVars(vars config.Variables, includeOSEnvVars bool) (map[str
if !found {
return nil, fmt.Errorf("failed to lookup config reference %s for %s", e.ConfigRef, e.Name)
}
envVars[e.Name] = anyToString(value)
envVars[e.Name] = utils.AnyToString(value)
}
return envVars, nil
}

func anyToString(value any) string {
switch v := value.(type) {
case string:
return v
case int:
return fmt.Sprintf("%d", v)
case bool:
return fmt.Sprintf("%t", v)
default:
return fmt.Sprintf("%v", v)
}
}
7 changes: 7 additions & 0 deletions tooling/templatize/pkg/pipeline/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ type step struct {
Template string `yaml:"template"`
Parameters string `yaml:"parameters"`
DependsOn []string `yaml:"dependsOn"`
DryRun dryRun `yaml:"dryRun"`
}

type dryRun struct {
EnvVars []EnvVar `yaml:"envVars"`
Command []string `yaml:"command"`
}

type EnvVar struct {
Name string `yaml:"name"`
ConfigRef string `yaml:"configRef"`
Value string `yaml:"value"`
}
16 changes: 16 additions & 0 deletions tooling/templatize/pkg/utils/typing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package utils

import "fmt"

func AnyToString(value any) string {
switch v := value.(type) {
case string:
return v
case int:
return fmt.Sprintf("%d", v)
case bool:
return fmt.Sprintf("%t", v)
default:
return fmt.Sprintf("%v", v)
}
}
4 changes: 4 additions & 0 deletions tooling/templatize/testdata/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
deploy:
echo ${DRY_RUN}

.PHONE: deploy
4 changes: 2 additions & 2 deletions tooling/templatize/testdata/config.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
$schema: schema.json
defaults:
region: {{ .ctx.region }}
serviceClusterSubscription: hcp-{{ .ctx.region }}
managementClusterSubscription: hcp-{{ .ctx.region }}
serviceClusterSubscription: ARO Hosted Control Planes (EA Subscription 1)
managementClusterSubscription: ARO Hosted Control Planes (EA Subscription 1)
globalRG: global
regionRG: hcp-underlay-{{ .ctx.regionShort }}
serviceClusterRG: hcp-underlay-{{ .ctx.regionShort }}-svc
Expand Down
11 changes: 6 additions & 5 deletions tooling/templatize/testdata/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ resourceGroups:
- name: deploy
action: Shell
command: ["make", "deploy"]
dryRun:
envVars:
- name: DRY_RUN
value: "true"
command: ["make", "-n", "deploy"]
env:
- name: MAESTRO_IMAGE
configRef: maestro_image
- name: svc
action: ARM
template: templates/svc-cluster.bicep
parameters: test.bicepparam
configRef: clusterService.imageTag

0 comments on commit 031d7b2

Please sign in to comment.