Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: treat pipeline flag defaults consistently when used as command f… #2798

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 18 additions & 35 deletions cmd/run_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/loft-sh/devspace/pkg/devspace/hook"
"github.com/loft-sh/devspace/pkg/devspace/kill"
"github.com/loft-sh/devspace/pkg/devspace/kubectl"
"github.com/loft-sh/devspace/pkg/devspace/pipeline"
pipelinepkg "github.com/loft-sh/devspace/pkg/devspace/pipeline"
"github.com/loft-sh/devspace/pkg/devspace/pipeline/types"
"github.com/loft-sh/devspace/pkg/devspace/plugin"
"github.com/loft-sh/devspace/pkg/devspace/upgrade"
Expand Down Expand Up @@ -100,51 +100,34 @@ func (cmd *RunPipelineCmd) AddPipelineFlags(f factory.Factory, command *cobra.Co
usage = "Flag " + pipelineFlag.Name
}

var ok bool
if pipelineFlag.Type == "" || pipelineFlag.Type == latest.PipelineFlagTypeBoolean {
val := false
if pipelineFlag.Default != nil {
val, ok = pipelineFlag.Default.(bool)
if !ok {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not a boolean", pipelineFlag.Name, pipelineFlag.Default)
continue
}
val, err := pipelinepkg.GetDefaultValue(pipelineFlag)
if err != nil {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not a boolean", pipelineFlag.Name, pipelineFlag.Default)
}

command.Flags().BoolP(pipelineFlag.Name, pipelineFlag.Short, val, usage)
command.Flags().BoolP(pipelineFlag.Name, pipelineFlag.Short, val.(bool), usage)
} else if pipelineFlag.Type == latest.PipelineFlagTypeString {
val := ""
if pipelineFlag.Default != nil {
val, ok = pipelineFlag.Default.(string)
if !ok {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not a string", pipelineFlag.Name, pipelineFlag.Default)
continue
}
val, err := pipelinepkg.GetDefaultValue(pipelineFlag)
if err != nil {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not a string", pipelineFlag.Name, pipelineFlag.Default)
}

command.Flags().StringP(pipelineFlag.Name, pipelineFlag.Short, val, usage)
command.Flags().StringP(pipelineFlag.Name, pipelineFlag.Short, val.(string), usage)
} else if pipelineFlag.Type == latest.PipelineFlagTypeInteger {
val := 0
if pipelineFlag.Default != nil {
val, ok = pipelineFlag.Default.(int)
if !ok {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not an integer", pipelineFlag.Name, pipelineFlag.Default)
continue
}
val, err := pipelinepkg.GetDefaultValue(pipelineFlag)
if err != nil {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not an integer", pipelineFlag.Name, pipelineFlag.Default)
}

command.Flags().IntP(pipelineFlag.Name, pipelineFlag.Short, val, usage)
command.Flags().IntP(pipelineFlag.Name, pipelineFlag.Short, val.(int), usage)
} else if pipelineFlag.Type == latest.PipelineFlagTypeStringArray {
val := []string{}
if pipelineFlag.Default != nil {
val, ok = pipelineFlag.Default.([]string)
if !ok {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not a string array", pipelineFlag.Name, pipelineFlag.Default)
continue
}
val, err := pipelinepkg.GetDefaultValue(pipelineFlag)
if err != nil {
f.GetLog().Errorf("Error parsing default value for flag %s: %#v is not a string array", pipelineFlag.Name, pipelineFlag.Default)
}

command.Flags().StringSliceP(pipelineFlag.Name, pipelineFlag.Short, val, usage)
command.Flags().StringSliceP(pipelineFlag.Name, pipelineFlag.Short, val.([]string), usage)
}
}
}
Expand Down Expand Up @@ -463,7 +446,7 @@ func runPipeline(ctx devspacecontext.Context, args []string, options *CommandOpt
dependencyRegistry := registry.NewDependencyRegistry(ctx.Config().Config().Name, options.DeployOptions.Render)

// get deploy pipeline
pipe := pipeline.NewPipeline(ctx.Config().Config().Name, devPodManager, dependencyRegistry, configPipeline, options.Options)
pipe := pipelinepkg.NewPipeline(ctx.Config().Config().Name, devPodManager, dependencyRegistry, configPipeline, options.Options)
kill.SetStopFunction(func(message string) {
if message != "" {
ctx.Log().WriteString(logrus.FatalLevel, "\n"+ansi.Color("fatal", "red+b")+" "+message+"\n")
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/configuration/pipelines/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ pipelines:
short: e
type: stringArray
run: |-
extraEnv=$(get_flag "env") # Retrieve the value of the `env` flag and store it in a variable
echo ${extraEnv[1]}
extraEnv=($(get_flag "env")) # Retrieve the value of the `env` flag and store it in an array variable
echo ${extraEnv[0]} # Arrays are zero indexed

TERMINAL_ENABLED=true
if [ $(get_flag "logs") == "true" ]; then # Test if --logs/-l flag is used or not
Expand Down
139 changes: 139 additions & 0 deletions e2e/tests/pipelines/pipelines.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ var _ = DevSpaceDescribe("pipelines", func() {
framework.ExpectLocalFileContentsImmediately("other.txt", "test\n")
framework.ExpectLocalFileContentsImmediately("other2.txt", "false\n")
framework.ExpectLocalFileContentsImmediately("other3.txt", "true\n")
framework.ExpectLocalFileContentsImmediately("other4-0.txt", "one\n")
framework.ExpectLocalFileContentsImmediately("other4-1.txt", "two\n")
framework.ExpectLocalFileContentsImmediately("other-profile.txt", "profile1\n")
framework.ExpectLocalFileContentsImmediately("dep1-test.txt", "test\n")
framework.ExpectLocalFileContentsImmediately("dep1-test2.txt", "true\n")
Expand All @@ -85,6 +87,143 @@ var _ = DevSpaceDescribe("pipelines", func() {
framework.ExpectLocalFileContentsImmediately("dep1-other-profile.txt", "profile1\n")
})

ginkgo.It("should resolve pipeline override array flags", func() {
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
framework.ExpectNoError(err)
defer framework.CleanupTempDir(initialDir, tempDir)

ns, err := kubeClient.CreateNamespace("pipelines")
framework.ExpectNoError(err)
defer framework.ExpectDeleteNamespace(kubeClient, ns)

rootCmd := cmd.NewRootCmd(f)
persistentFlags := rootCmd.PersistentFlags()
globalFlags := flags.SetGlobalFlags(persistentFlags)
globalFlags.NoWarn = true
globalFlags.Namespace = ns
globalFlags.Profiles = []string{"profile1"}

cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{
"other": "test",
"other2": "false",
"other3": "true",
"other4": "three four",
})

devCmd := &cmd.RunPipelineCmd{
GlobalFlags: globalFlags,
Pipeline: "other",
Ctx: cmdCtx,
}
err = devCmd.RunDefault(f)
framework.ExpectNoError(err)

framework.ExpectLocalFileContentsImmediately("other.txt", "test\n")
framework.ExpectLocalFileContentsImmediately("other2.txt", "false\n")
framework.ExpectLocalFileContentsImmediately("other3.txt", "true\n")
framework.ExpectLocalFileContentsImmediately("other-profile.txt", "profile1\n")
framework.ExpectLocalFileContentsImmediately("other4-0.txt", "three\n")
framework.ExpectLocalFileContentsImmediately("other4-1.txt", "four\n")
})

ginkgo.It("should resolve pipeline override with --set-flags", func() {
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
framework.ExpectNoError(err)
defer framework.CleanupTempDir(initialDir, tempDir)

ns, err := kubeClient.CreateNamespace("pipelines")
framework.ExpectNoError(err)
defer framework.ExpectDeleteNamespace(kubeClient, ns)

rootCmd := cmd.NewRootCmd(f)
persistentFlags := rootCmd.PersistentFlags()
globalFlags := flags.SetGlobalFlags(persistentFlags)
globalFlags.NoWarn = true
globalFlags.Namespace = ns
globalFlags.Profiles = []string{"profile1"}

cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{})

devCmd := &cmd.RunPipelineCmd{
GlobalFlags: globalFlags,
Pipeline: "other-override",
Ctx: cmdCtx,
}
err = devCmd.RunDefault(f)
framework.ExpectNoError(err)

framework.ExpectLocalFileContentsImmediately("other.txt", "test\n")
framework.ExpectLocalFileContentsImmediately("other2.txt", "true\n")
framework.ExpectLocalFileContentsImmediately("other3.txt", "true\n")
framework.ExpectLocalFileContentsImmediately("other-profile.txt", "profile1\n")
framework.ExpectLocalFileContentsImmediately("other4-0.txt", "five\n")
framework.ExpectLocalFileContentsImmediately("other4-1.txt", "six\n")
})

ginkgo.It("should resolve dependency pipeline flag defaults", func() {
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
framework.ExpectNoError(err)
defer framework.CleanupTempDir(initialDir, tempDir)

ns, err := kubeClient.CreateNamespace("pipelines")
framework.ExpectNoError(err)
defer framework.ExpectDeleteNamespace(kubeClient, ns)

rootCmd := cmd.NewRootCmd(f)
persistentFlags := rootCmd.PersistentFlags()
globalFlags := flags.SetGlobalFlags(persistentFlags)
globalFlags.NoWarn = true
globalFlags.Namespace = ns
globalFlags.Profiles = []string{"profile1"}

cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{})

devCmd := &cmd.RunPipelineCmd{
GlobalFlags: globalFlags,
Pipeline: "arr-dep1",
Ctx: cmdCtx,
}
err = devCmd.RunDefault(f)
framework.ExpectNoError(err)

framework.ExpectLocalFileContentsImmediately("arr-0.txt", "one")
framework.ExpectLocalFileContentsImmediately("arr-1.txt", "two")
})

ginkgo.It("should resolve dependency pipeline flag defaults", func() {
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
framework.ExpectNoError(err)
defer framework.CleanupTempDir(initialDir, tempDir)

ns, err := kubeClient.CreateNamespace("pipelines")
framework.ExpectNoError(err)
defer framework.ExpectDeleteNamespace(kubeClient, ns)

rootCmd := cmd.NewRootCmd(f)
persistentFlags := rootCmd.PersistentFlags()
globalFlags := flags.SetGlobalFlags(persistentFlags)
globalFlags.NoWarn = true
globalFlags.Namespace = ns
globalFlags.Profiles = []string{"profile1"}

cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{})

devCmd := &cmd.RunPipelineCmd{
GlobalFlags: globalFlags,
Pipeline: "arr-dep1-override",
Ctx: cmdCtx,
}
err = devCmd.RunDefault(f)
framework.ExpectNoError(err)

framework.ExpectLocalFileContentsImmediately("arr-0.txt", "three")
framework.ExpectLocalFileContentsImmediately("arr-1.txt", "")
})

ginkgo.It("should exec container", func() {
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/exec_container")
framework.ExpectNoError(err)
Expand Down
12 changes: 12 additions & 0 deletions e2e/tests/pipelines/testdata/flags/dep1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,15 @@ pipelines:
echo $(get_flag test3) > dep1-test2.txt
echo $(get_flag profile) > dep1-dev-profile.txt
run_pipelines other --set-flag other2=false

array:
flags:
- name: arr
type: stringArray
default:
- one
- two
run: |-
arr=($(get_flag arr))
echo -n ${arr[0]} > arr-0.txt
echo -n ${arr[1]} > arr-1.txt
21 changes: 21 additions & 0 deletions e2e/tests/pipelines/testdata/flags/devspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ pipelines:
default: true
- name: other3
default: true
- name: other4
type: stringArray
default:
- one
- two
run: |-
if get_flag test; then
exit 1
Expand All @@ -24,6 +29,22 @@ pipelines:
echo $(get_flag other2) > other2.txt
echo $(get_flag other3) > other3.txt
echo $(get_flag profile) > other-profile.txt

other4=($(get_flag other4))
echo ${other4[0]} > other4-0.txt
echo ${other4[1]} > other4-1.txt

other-override:
run: |-
run_pipelines other --set-flag other4=five --set-flag other4=six

arr-dep1:
run: |-
run_dependency_pipelines dep1 --pipeline array

arr-dep1-override:
run: |-
run_dependency_pipelines dep1 --pipeline array --set-flag arr=three

dev:
flags:
Expand Down
77 changes: 77 additions & 0 deletions pkg/devspace/pipeline/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package pipeline

import (
"fmt"
"strconv"

"github.com/loft-sh/devspace/pkg/devspace/config/versions/latest"
)

func GetDefaultValue(pipelineFlag latest.PipelineFlag) (interface{}, error) {
var ok bool

switch pipelineFlag.Type {
case "", latest.PipelineFlagTypeBoolean:
val := false
if pipelineFlag.Default != nil {
val, ok = pipelineFlag.Default.(bool)
if !ok {
return nil, fmt.Errorf(" default is not a boolean")
}
}
return val, nil
case latest.PipelineFlagTypeString:
val := ""
if pipelineFlag.Default != nil {
val, ok = pipelineFlag.Default.(string)
if !ok {
return nil, fmt.Errorf("default is not a string")
}
}
return val, nil
case latest.PipelineFlagTypeInteger:
val := 0
if pipelineFlag.Default != nil {
switch pipelineFlag.Default.(type) {
case float64:
floatVal, ok := pipelineFlag.Default.(float64)
if !ok {
return nil, fmt.Errorf("default is not an integer")
}
return int(floatVal), nil
case int:
intVal, ok := pipelineFlag.Default.(int)
if !ok {
return nil, fmt.Errorf("default is not an integer")
}
return intVal, nil
case string:
strVal, ok := pipelineFlag.Default.(string)
if !ok {
return nil, fmt.Errorf("default is not an integer")
}
intVal, err := strconv.ParseInt(strVal, 10, 0)
if err != nil {
return nil, err
}
return int(intVal), nil
}
return nil, fmt.Errorf("default is not an integer")
}
return val, nil
case latest.PipelineFlagTypeStringArray:
val := []string{}
if pipelineFlag.Default != nil {
for _, anyVal := range pipelineFlag.Default.([]interface{}) {
strVal, ok := anyVal.(string)
if !ok {
return nil, fmt.Errorf("default is not a string array")
}
val = append(val, strVal)
}
}
return val, nil
}

return nil, fmt.Errorf("unsupported flag type")
}
Loading
Loading