diff --git a/docs/shp_build_create.md b/docs/shp_build_create.md index f597cc1e..d77966c8 100644 --- a/docs/shp_build_create.md +++ b/docs/shp_build_create.md @@ -19,6 +19,7 @@ shp build create [flags] ``` -e, --env stringArray specify a key-value pair for an environment variable to set for the build container (default []) -h, --help help for create + --node-selector stringArray set of key-value pairs that correspond to labels of a node to match (default []) --output-image string image employed during the building process --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 []) diff --git a/docs/shp_build_run.md b/docs/shp_build_run.md index 3d8c7871..bcec5fa7 100644 --- a/docs/shp_build_run.md +++ b/docs/shp_build_run.md @@ -22,6 +22,7 @@ shp build run [flags] -e, --env stringArray specify a key-value pair for an environment variable to set for the build container (default []) -F, --follow Start a build and watch its log until it completes or fails. -h, --help help for run + --node-selector stringArray set of key-value pairs that correspond to labels of a node to match (default []) --output-image string image employed during the building process --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 []) diff --git a/docs/shp_build_upload.md b/docs/shp_build_upload.md index a69270aa..befdcfe2 100644 --- a/docs/shp_build_upload.md +++ b/docs/shp_build_upload.md @@ -32,6 +32,7 @@ shp build upload [path/to/source|.] [flags] -e, --env stringArray specify a key-value pair for an environment variable to set for the build container (default []) -F, --follow Start a build and watch its log until it completes or fails. -h, --help help for upload + --node-selector stringArray set of key-value pairs that correspond to labels of a node to match (default []) --output-image string image employed during the building process --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 []) diff --git a/docs/shp_buildrun_create.md b/docs/shp_buildrun_create.md index 2dd6e1f3..0cdcc6de 100644 --- a/docs/shp_buildrun_create.md +++ b/docs/shp_buildrun_create.md @@ -21,6 +21,7 @@ shp buildrun create [flags] --buildref-name string name of build resource to reference -e, --env stringArray specify a key-value pair for an environment variable to set for the build container (default []) -h, --help help for create + --node-selector stringArray set of key-value pairs that correspond to labels of a node to match (default []) --output-image string image employed during the building process --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 []) diff --git a/pkg/shp/flags/build.go b/pkg/shp/flags/build.go index 0549e9bc..4b6b2cb6 100644 --- a/pkg/shp/flags/build.go +++ b/pkg/shp/flags/build.go @@ -44,6 +44,7 @@ func BuildSpecFromFlags(flags *pflag.FlagSet) (*buildv1beta1.BuildSpec, *string, TTLAfterFailed: &metav1.Duration{}, TTLAfterSucceeded: &metav1.Duration{}, }, + NodeSelector: map[string]string{}, } sourceFlags(flags, spec.Source) @@ -55,7 +56,7 @@ func BuildSpecFromFlags(flags *pflag.FlagSet) (*buildv1beta1.BuildSpec, *string, imageLabelsFlags(flags, spec.Output.Labels) imageAnnotationsFlags(flags, spec.Output.Annotations) buildRetentionFlags(flags, spec.Retention) - + buildNodeSelectorFlags(flags, spec.NodeSelector) var dockerfile, builderImage string dockerfileFlags(flags, &dockerfile) builderImageFlag(flags, &builderImage) diff --git a/pkg/shp/flags/build_test.go b/pkg/shp/flags/build_test.go index 454b1e19..1c351b52 100644 --- a/pkg/shp/flags/build_test.go +++ b/pkg/shp/flags/build_test.go @@ -55,6 +55,7 @@ func TestBuildSpecFromFlags(t *testing.T) { Duration: 30 * time.Minute, }, }, + NodeSelector: map[string]string{"kubernetes.io/hostname": "worker-1"}, } cmd := &cobra.Command{} @@ -115,6 +116,13 @@ func TestBuildSpecFromFlags(t *testing.T) { g.Expect(expected.Output).To(o.Equal(spec.Output), "spec.output") }) + t.Run(".spec.nodeSelector", func(_ *testing.T) { + err := flags.Set(NodeSelectorFlag, "kubernetes.io/hostname=worker-1") + g.Expect(err).To(o.BeNil()) + // g.Expect(expected.NodeSelector).To(o.HaveKeyWithValue("kubernetes.io/hostname",spec.NodeSelector["kubernetes.io/hostname"]), ".spec.nodeSelector") + g.Expect(expected.NodeSelector).To(o.Equal(spec.NodeSelector), ".spec.nodeSelector") + }) + t.Run(".spec.timeout", func(_ *testing.T) { err := flags.Set(TimeoutFlag, expected.Timeout.Duration.String()) g.Expect(err).To(o.BeNil()) diff --git a/pkg/shp/flags/buildrun.go b/pkg/shp/flags/buildrun.go index 7181030d..30459a8a 100644 --- a/pkg/shp/flags/buildrun.go +++ b/pkg/shp/flags/buildrun.go @@ -28,6 +28,7 @@ func BuildRunSpecFromFlags(flags *pflag.FlagSet) *buildv1beta1.BuildRunSpec { TTLAfterFailed: &metav1.Duration{}, TTLAfterSucceeded: &metav1.Duration{}, }, + NodeSelector: map[string]string{}, } buildRefFlags(flags, &spec.Build) @@ -39,7 +40,7 @@ func BuildRunSpecFromFlags(flags *pflag.FlagSet) *buildv1beta1.BuildRunSpec { imageLabelsFlags(flags, spec.Output.Labels) imageAnnotationsFlags(flags, spec.Output.Annotations) buildRunRetentionFlags(flags, spec.Retention) - + buildNodeSelectorFlags(flags, spec.NodeSelector) return spec } diff --git a/pkg/shp/flags/buildrun_test.go b/pkg/shp/flags/buildrun_test.go index 3459bc90..073a91f1 100644 --- a/pkg/shp/flags/buildrun_test.go +++ b/pkg/shp/flags/buildrun_test.go @@ -38,6 +38,7 @@ func TestBuildRunSpecFromFlags(t *testing.T) { Duration: 30 * time.Minute, }, }, + NodeSelector: map[string]string{"kubernetes.io/hostname": "worker-1"}, } cmd := &cobra.Command{} @@ -75,6 +76,13 @@ func TestBuildRunSpecFromFlags(t *testing.T) { g.Expect(*expected.Output).To(o.Equal(*spec.Output), "spec.output") }) + t.Run(".spec.nodeSelector", func(_ *testing.T) { + err := flags.Set(NodeSelectorFlag, "kubernetes.io/hostname=worker-1") + g.Expect(err).To(o.BeNil()) + // g.Expect(expected.NodeSelector).To(o.HaveKeyWithValue("kubernetes.io/hostname",spec.NodeSelector["kubernetes.io/hostname"]), ".spec.nodeSelector") + g.Expect(expected.NodeSelector).To(o.Equal(spec.NodeSelector), ".spec.nodeSelector") + }) + t.Run(".spec.retention.ttlAfterFailed", func(_ *testing.T) { err := flags.Set(RetentionTTLAfterFailedFlag, expected.Retention.TTLAfterFailed.Duration.String()) g.Expect(err).To(o.BeNil()) diff --git a/pkg/shp/flags/flags.go b/pkg/shp/flags/flags.go index bdb371f0..35249073 100644 --- a/pkg/shp/flags/flags.go +++ b/pkg/shp/flags/flags.go @@ -64,6 +64,8 @@ const ( RetentionTTLAfterFailedFlag = "retention-ttl-after-failed" // RetentionTTLAfterSucceededFlag command-line flag. RetentionTTLAfterSucceededFlag = "retention-ttl-after-succeeded" + // NodeSelectorFlag command-line flag. + NodeSelectorFlag = "node-selector" ) // sourceFlags flags for ".spec.source" @@ -259,6 +261,11 @@ func serviceAccountFlags(flags *pflag.FlagSet, sa *string) { } +// buildNodeSelectorFlags registers flags for adding BuildSpec.NodeSelector +func buildNodeSelectorFlags(flags *pflag.FlagSet, nodeSelectorLabels map[string]string) { + flags.Var(NewMapValue(nodeSelectorLabels), NodeSelectorFlag, "set of key-value pairs that correspond to labels of a node to match") +} + // envFlags registers flags for adding corev1.EnvVars. func envFlags(flags *pflag.FlagSet, envs *[]corev1.EnvVar) { flags.VarP( diff --git a/test/e2e/node-selector.bats b/test/e2e/node-selector.bats new file mode 100644 index 00000000..cfcf868d --- /dev/null +++ b/test/e2e/node-selector.bats @@ -0,0 +1,114 @@ +#!/usr/bin/env bats + +source test/e2e/helpers.sh + +setup() { + load 'bats/support/load' + load 'bats/assert/load' + load 'bats/file/load' +} + +teardown() { + run kubectl delete builds.shipwright.io --all + run kubectl delete buildruns.shipwright.io --all +} + +@test "shp build create --node-selector single label" { + # generate random names for our build + build_name=$(random_name) + + # create a Build with node selector + run shp build create ${build_name} --source-git-url=https://github.com/shipwright-io/sample-go --output-image=my-fake-image --node-selector="kubernetes.io/hostname=node-1" + assert_success + + # ensure that the build was successfully created + assert_output --partial "Created build \"${build_name}\"" + + # get the jsonpath of Build object .spec.nodeSelector + run kubectl get builds.shipwright.io/${build_name} -ojsonpath="{.spec.nodeSelector}" + assert_success + + assert_output '{"kubernetes.io/hostname":"node-1"}' +} + +@test "shp build create --node-selector multiple labels" { + # generate random names for our build + build_name=$(random_name) + + # create a Build with node selector + run shp build create ${build_name} --source-git-url=https://github.com/shipwright-io/sample-go --output-image=my-fake-image --node-selector="kubernetes.io/hostname=node-1" --node-selector="kubernetes.io/os=linux" + assert_success + + # ensure that the build was successfully created + assert_output --partial "Created build \"${build_name}\"" + + # get the jsonpath of Build object .spec.nodeSelector + run kubectl get builds.shipwright.io/${build_name} -ojsonpath="{.spec.nodeSelector}" + assert_success + + assert_output --partial '"kubernetes.io/hostname":"node-1"' + assert_output --partial '"kubernetes.io/os":"linux"' +} + +@test "shp buildrun create --node-selector single label" { + # generate random names for our buildrun + buildrun_name=$(random_name) + build_name=$(random_name) + + # create a Build with node selector + run shp buildrun create ${buildrun_name} --buildref-name=${build_name} --node-selector="kubernetes.io/hostname=node-1" + assert_success + + # ensure that the build was successfully created + assert_output --partial "BuildRun created \"${buildrun_name}\" for Build \"${build_name}\"" + + # get the jsonpath of Build object .spec.nodeSelector + run kubectl get buildruns.shipwright.io/${buildrun_name} -ojsonpath="{.spec.nodeSelector}" + assert_success + + assert_output '{"kubernetes.io/hostname":"node-1"}' +} + +@test "shp buildrun create --node-selector multiple labels" { + # generate random names for our buildrun + buildrun_name=$(random_name) + build_name=$(random_name) + + # create a Build with node selector + run shp buildrun create ${buildrun_name} --buildref-name=${build_name} --node-selector="kubernetes.io/hostname=node-1" --node-selector="kubernetes.io/os=linux" + assert_success + + # ensure that the build was successfully created + assert_output --partial "BuildRun created \"${buildrun_name}\" for Build \"${build_name}\"" + + # get the jsonpath of Build object .spec.nodeSelector + run kubectl get buildruns.shipwright.io/${buildrun_name} -ojsonpath="{.spec.nodeSelector}" + assert_success + + assert_output --partial '"kubernetes.io/hostname":"node-1"' + assert_output --partial '"kubernetes.io/os":"linux"' +} + + +@test "shp build run --node-selector set" { + # generate random names for our build + build_name=$(random_name) + + # create a Build with node selector + run shp build create ${build_name} --source-git-url=https://github.com/shipwright-io/sample-go --output-image=my-fake-image + assert_success + + # ensure that the build was successfully created + assert_output --partial "Created build \"${build_name}\"" + + # get the build object + run kubectl get builds.shipwright.io/${build_name} + assert_success + + run shp build run ${build_name} --node-selector="kubernetes.io/hostname=node-1" + + # get the jsonpath of Build object .spec.nodeSelector + run kubectl get buildruns.shipwright.io -ojsonpath='{.items[*].spec.nodeSelector}' + assert_success + assert_output --partial '"kubernetes.io/hostname":"node-1"' +} \ No newline at end of file