diff --git a/docs/shp_build_create.md b/docs/shp_build_create.md index 2e7f47925..da9b7ff53 100644 --- a/docs/shp_build_create.md +++ b/docs/shp_build_create.md @@ -27,6 +27,7 @@ shp build create [flags] --output-image-annotation stringArray specify a set of key-value pairs that correspond to annotations to set on the output image (default []) --output-image-label stringArray specify a set of key-value pairs that correspond to labels to set on the output image (default []) --output-insecure flag to indicate an insecure container registry + --param-value stringArray set of key-value pairs to pass as parameters to the buildStrategy (default []) --retention-failed-limit uint number of failed BuildRuns to be kept (default 65535) --retention-succeeded-limit uint number of succeeded BuildRuns to be kept (default 65535) --retention-ttl-after-failed duration duration to delete a failed BuildRun after completion diff --git a/docs/shp_build_run.md b/docs/shp_build_run.md index 3f0bee987..8a7c19988 100644 --- a/docs/shp_build_run.md +++ b/docs/shp_build_run.md @@ -28,6 +28,7 @@ shp build run [flags] --output-image-annotation stringArray specify a set of key-value pairs that correspond to annotations to set on the output image (default []) --output-image-label stringArray specify a set of key-value pairs that correspond to labels to set on the output image (default []) --output-insecure flag to indicate an insecure container registry + --param-value stringArray set of key-value pairs to pass as parameters to the buildStrategy (default []) --retention-ttl-after-failed duration duration to delete the BuildRun after it failed --retention-ttl-after-succeeded duration duration to delete the BuildRun after it succeeded --sa-generate generate a Kubernetes service-account for the build diff --git a/docs/shp_build_upload.md b/docs/shp_build_upload.md index d69e22732..12aa7a951 100644 --- a/docs/shp_build_upload.md +++ b/docs/shp_build_upload.md @@ -38,6 +38,7 @@ shp build upload [path/to/source|.] [flags] --output-image-annotation stringArray specify a set of key-value pairs that correspond to annotations to set on the output image (default []) --output-image-label stringArray specify a set of key-value pairs that correspond to labels to set on the output image (default []) --output-insecure flag to indicate an insecure container registry + --param-value stringArray set of key-value pairs to pass as parameters to the buildStrategy (default []) --retention-ttl-after-failed duration duration to delete the BuildRun after it failed --retention-ttl-after-succeeded duration duration to delete the BuildRun after it succeeded --sa-generate generate a Kubernetes service-account for the build diff --git a/docs/shp_buildrun_create.md b/docs/shp_buildrun_create.md index c4d17f823..eb26ca0ce 100644 --- a/docs/shp_buildrun_create.md +++ b/docs/shp_buildrun_create.md @@ -27,6 +27,7 @@ shp buildrun create [flags] --output-image-annotation stringArray specify a set of key-value pairs that correspond to annotations to set on the output image (default []) --output-image-label stringArray specify a set of key-value pairs that correspond to labels to set on the output image (default []) --output-insecure flag to indicate an insecure container registry + --param-value stringArray set of key-value pairs to pass as parameters to the buildStrategy (default []) --retention-ttl-after-failed duration duration to delete the BuildRun after it failed --retention-ttl-after-succeeded duration duration to delete the BuildRun after it succeeded --sa-generate generate a Kubernetes service-account for the build diff --git a/pkg/shp/flags/build.go b/pkg/shp/flags/build.go index 07a285c0b..2464e2e32 100644 --- a/pkg/shp/flags/build.go +++ b/pkg/shp/flags/build.go @@ -55,6 +55,7 @@ func BuildSpecFromFlags(flags *pflag.FlagSet) *buildv1alpha1.BuildSpec { imageFlags(flags, "output", &spec.Output) timeoutFlags(flags, spec.Timeout) envFlags(flags, &spec.Env) + paramValueFlag(flags, &spec.ParamValues) imageLabelsFlags(flags, spec.Output.Labels) imageAnnotationsFlags(flags, spec.Output.Annotations) buildRetentionFlags(flags, spec.Retention) diff --git a/pkg/shp/flags/buildrun.go b/pkg/shp/flags/buildrun.go index 8dc897e6a..33a0f011c 100644 --- a/pkg/shp/flags/buildrun.go +++ b/pkg/shp/flags/buildrun.go @@ -38,6 +38,7 @@ func BuildRunSpecFromFlags(flags *pflag.FlagSet) *buildv1alpha1.BuildRunSpec { timeoutFlags(flags, spec.Timeout) imageFlags(flags, "output", spec.Output) envFlags(flags, &spec.Env) + paramValueFlag(flags, &spec.ParamValues) imageLabelsFlags(flags, spec.Output.Labels) imageAnnotationsFlags(flags, spec.Output.Annotations) buildRunRetentionFlags(flags, spec.Retention) diff --git a/pkg/shp/flags/flags.go b/pkg/shp/flags/flags.go index 7b2eefac0..0d90701ab 100644 --- a/pkg/shp/flags/flags.go +++ b/pkg/shp/flags/flags.go @@ -46,6 +46,8 @@ const ( OutputInsecureFlag = "output-insecure" // OutputCredentialsSecretFlag command-line flag. OutputCredentialsSecretFlag = "output-credentials-secret" // #nosec G101 + // ParameterValueFlag command-line flag. + ParamValueFlag = "param-value" // ServiceAccountNameFlag command-line flag. ServiceAccountNameFlag = "sa-name" // ServiceAccountGenerateFlag command-line flag. @@ -212,6 +214,16 @@ func envFlags(flags *pflag.FlagSet, envs *[]corev1.EnvVar) { ) } +// parameterValueFlag registers flags for adding BuildSpec.ParamValues +func paramValueFlag(flags *pflag.FlagSet, paramValue *[]buildv1alpha1.ParamValue) { + flags.VarP( + NewParamArrayValue(paramValue), + ParamValueFlag, + "", + "set of key-value pairs to pass as parameters to the buildStrategy", + ) +} + // imageLabelsFlags registers flags for output image labels. func imageLabelsFlags(flags *pflag.FlagSet, labels map[string]string) { flags.VarP( diff --git a/pkg/shp/flags/param_value.go b/pkg/shp/flags/param_value.go new file mode 100644 index 000000000..1f21aed21 --- /dev/null +++ b/pkg/shp/flags/param_value.go @@ -0,0 +1,50 @@ +package flags + +import ( + "fmt" + + buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" +) + +// ParamArrayValue implements pflag.Value interface, in order to store ParamValue key-value +// pairs used on Shipwright's BuildSpec. +type ParamArrayValue struct { + params *[]buildv1alpha1.ParamValue // pointer to the slice of ParamValue +} + +// String prints out the string representation of the slice of EnvVar objects. +func (p *ParamArrayValue) String() string { + slice := []string{} + for _, e := range *p.params { + slice = append(slice, fmt.Sprintf("%s=%v", e.Name, e.Value)) + } + csv, _ := writeAsCSV(slice) + return fmt.Sprintf("[%s]", csv) +} + +// Set receives a key-value entry separated by equal sign ("="). +func (p *ParamArrayValue) Set(value string) error { + k, v, err := splitKeyValue(value) + if err != nil { + return err + } + for _, e := range *p.params { + if k == e.Name { + return fmt.Errorf("environment variable '%s' is already set", k) + } + } + *p.params = append(*p.params, buildv1alpha1.ParamValue{Name: k, SingleValue: &buildv1alpha1.SingleValue{Value: &v}}) + return nil +} + +// Type analogous to the pflag "stringArray" type, where each flag entry will be tranlated to a +// single array (slice) entry, therefore the comma (",") is accepted as part of the value, as any +// other special character. +func (p *ParamArrayValue) Type() string { + return "stringArray" +} + +// NewCoreEnvVarArrayValue instantiate a ParamValSliceValue sharing the EnvVar pointer. +func NewParamArrayValue(params *[]buildv1alpha1.ParamValue) *ParamArrayValue { + return &ParamArrayValue{params: params} +} diff --git a/pkg/shp/flags/param_value_test.go b/pkg/shp/flags/param_value_test.go new file mode 100644 index 000000000..eddad09d6 --- /dev/null +++ b/pkg/shp/flags/param_value_test.go @@ -0,0 +1,60 @@ +package flags + +import ( + "errors" + "testing" + + "github.com/onsi/gomega" + buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" +) + +func TestNewParamArrayValue(t *testing.T) { + g := gomega.NewWithT(t) + + testCases := map[string]struct { + paramPassed string + paramKey string + paramValue string + expectedErr error + }{ + "simpleKeyValPair": { + paramPassed: "dockerfile=Dockerfile", + paramKey: "dockerfile", + paramValue: "Dockerfile", + expectedErr: nil, + }, + "specialCharKeyValPair": { + paramPassed: "b=cd=e", + paramKey: "b", + paramValue: "cd=e", + expectedErr: nil, + }, + "noEquals": { + paramPassed: "bc", + expectedErr: errors.New("informed value 'bc' is not in key=value format"), + }, + "withSpaceVal": { + paramPassed: "b=c d", + paramKey: "b", + paramValue: "c d", + expectedErr: nil, + }, + } + for tName, tCase := range testCases { + t.Run(tName, func(_ *testing.T) { + buildSpec := buildv1alpha1.BuildSpec{} + buildParamVal := NewParamArrayValue(&buildSpec.ParamValues) + + err := buildParamVal.Set(tCase.paramPassed) + if tCase.expectedErr != nil { + g.Expect(err).To(gomega.Equal(tCase.expectedErr)) + return + } + g.Expect(err).To(gomega.BeNil()) + + paramVal := tCase.paramValue + g.Expect(buildSpec.ParamValues[0].Name).To(gomega.Equal(tCase.paramKey)) + g.Expect(buildSpec.ParamValues[0].Value).To(gomega.Equal(¶mVal)) + }) + } +}