From 8e08af206ab99e83bc4b2edfa64bb3aeed3a255c Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Thu, 11 May 2017 10:04:45 -0400 Subject: [PATCH] (WIP) Support valueFrom syntax for build env vars Trello card: https://trello.com/c/lDxfG8GO/1062-8-support-valuefrom-syntax-for-build-env-vars-techdebt --- ...om_openshift_origin_pkg_build_api_v1.proto | 5 - api/swagger-spec/oapi-v1.json | 10 +- api/swagger-spec/openshift-openapi-spec.json | 10 +- pkg/build/admission/defaults/admission.go | 29 +- .../admission/defaults/admission_test.go | 5 +- .../api/validation/validation_test.go | 14 +- pkg/build/api/types.go | 46 +-- pkg/build/api/v1/generated.proto | 5 - pkg/build/api/v1/swagger_doc.go | 10 +- pkg/build/api/v1/types.go | 5 - pkg/build/api/validation/validation.go | 4 +- pkg/build/api/validation/validation_test.go | 46 +-- .../controller/build/build_controller.go | 17 +- pkg/build/controller/common/util.go | 60 +++ pkg/build/generator/generator.go | 37 +- pkg/build/util/util.go | 56 +++ pkg/cmd/cli/cmd/set/env.go | 113 +----- .../bootstrappolicy/controller_policy.go | 1 + pkg/openapi/zz_generated.openapi.go | 10 +- pkg/util/env/env.go | 138 +++++++ test/extended/builds/valuefrom.go | 129 ++++++ test/extended/testdata/bindata.go | 373 ++++++++++++++++++ ...failed-docker-build-value-from-config.yaml | 48 +++ .../failed-sti-build-value-from-config.yaml | 49 +++ ...essful-docker-build-value-from-config.yaml | 56 +++ ...uccessful-sti-build-value-from-config.yaml | 57 +++ .../testdata/valuefrom/test-configmap.yaml | 6 + test/extended/testdata/valuefrom/test-is.json | 7 + .../testdata/valuefrom/test-secret.yaml | 8 + .../bootstrap_cluster_roles.yaml | 8 + 30 files changed, 1088 insertions(+), 274 deletions(-) create mode 100644 pkg/util/env/env.go create mode 100644 test/extended/builds/valuefrom.go create mode 100644 test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml create mode 100644 test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml create mode 100644 test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml create mode 100644 test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml create mode 100644 test/extended/testdata/valuefrom/test-configmap.yaml create mode 100644 test/extended/testdata/valuefrom/test-is.json create mode 100644 test/extended/testdata/valuefrom/test-secret.yaml diff --git a/api/protobuf-spec/github_com_openshift_origin_pkg_build_api_v1.proto b/api/protobuf-spec/github_com_openshift_origin_pkg_build_api_v1.proto index d86c2cf44798..f29b23a0e2ad 100644 --- a/api/protobuf-spec/github_com_openshift_origin_pkg_build_api_v1.proto +++ b/api/protobuf-spec/github_com_openshift_origin_pkg_build_api_v1.proto @@ -319,7 +319,6 @@ message BuildRequest { optional int64 lastVersion = 6; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 7; // triggeredBy describes which triggers started the most recent update to the @@ -586,7 +585,6 @@ message CustomBuildStrategy { optional k8s.io.kubernetes.pkg.api.v1.LocalObjectReference pullSecret = 2; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 3; // exposeDockerSocket will allow running Docker commands (and build Docker images) from @@ -622,7 +620,6 @@ message DockerBuildStrategy { optional bool noCache = 3; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 4; // forcePull describes if the builder should pull the images from registry prior to building. @@ -808,7 +805,6 @@ message JenkinsPipelineBuildStrategy { optional string jenkinsfile = 2; // env contains additional environment variables you want to pass into a build pipeline. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 3; } @@ -873,7 +869,6 @@ message SourceBuildStrategy { optional k8s.io.kubernetes.pkg.api.v1.LocalObjectReference pullSecret = 2; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 3; // scripts is the location of Source scripts diff --git a/api/swagger-spec/oapi-v1.json b/api/swagger-spec/oapi-v1.json index 718bc8a91f47..a05598641a5a 100644 --- a/api/swagger-spec/oapi-v1.json +++ b/api/swagger-spec/oapi-v1.json @@ -23726,7 +23726,7 @@ "items": { "$ref": "v1.EnvVar" }, - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported." + "description": "env contains additional environment variables you want to pass into a builder container." }, "forcePull": { "type": "boolean", @@ -23896,7 +23896,7 @@ "items": { "$ref": "v1.EnvVar" }, - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported." + "description": "env contains additional environment variables you want to pass into a builder container." }, "scripts": { "type": "string", @@ -23943,7 +23943,7 @@ "items": { "$ref": "v1.EnvVar" }, - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported." + "description": "env contains additional environment variables you want to pass into a builder container." }, "exposeDockerSocket": { "type": "boolean", @@ -24001,7 +24001,7 @@ "items": { "$ref": "v1.EnvVar" }, - "description": "env contains additional environment variables you want to pass into a build pipeline. ValueFrom is not supported." + "description": "env contains additional environment variables you want to pass into a build pipeline." } } }, @@ -24296,7 +24296,7 @@ "items": { "$ref": "v1.EnvVar" }, - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported." + "description": "env contains additional environment variables you want to pass into a builder container." }, "triggeredBy": { "type": "array", diff --git a/api/swagger-spec/openshift-openapi-spec.json b/api/swagger-spec/openshift-openapi-spec.json index c8ad2c760900..7f589b0be362 100644 --- a/api/swagger-spec/openshift-openapi-spec.json +++ b/api/swagger-spec/openshift-openapi-spec.json @@ -72897,7 +72897,7 @@ "$ref": "#/definitions/v1.DockerStrategyOptions" }, "env": { - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "description": "env contains additional environment variables you want to pass into a builder container.", "type": "array", "items": { "$ref": "#/definitions/v1.EnvVar" @@ -74251,7 +74251,7 @@ "type": "string" }, "env": { - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "description": "env contains additional environment variables you want to pass into a builder container.", "type": "array", "items": { "$ref": "#/definitions/v1.EnvVar" @@ -74859,7 +74859,7 @@ "type": "string" }, "env": { - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "description": "env contains additional environment variables you want to pass into a builder container.", "type": "array", "items": { "$ref": "#/definitions/v1.EnvVar" @@ -76722,7 +76722,7 @@ "description": "JenkinsPipelineBuildStrategy holds parameters specific to a Jenkins Pipeline build. This strategy is in tech preview.", "properties": { "env": { - "description": "env contains additional environment variables you want to pass into a build pipeline. ValueFrom is not supported.", + "description": "env contains additional environment variables you want to pass into a build pipeline.", "type": "array", "items": { "$ref": "#/definitions/v1.EnvVar" @@ -81535,7 +81535,7 @@ ], "properties": { "env": { - "description": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "description": "env contains additional environment variables you want to pass into a builder container.", "type": "array", "items": { "$ref": "#/definitions/v1.EnvVar" diff --git a/pkg/build/admission/defaults/admission.go b/pkg/build/admission/defaults/admission.go index 77ff256045f1..593620633ee4 100644 --- a/pkg/build/admission/defaults/admission.go +++ b/pkg/build/admission/defaults/admission.go @@ -9,6 +9,7 @@ import ( "github.com/openshift/origin/pkg/build/admission/defaults/api/validation" buildapi "github.com/openshift/origin/pkg/build/api" "github.com/openshift/origin/pkg/build/util" + buildutil "github.com/openshift/origin/pkg/build/util" configapi "github.com/openshift/origin/pkg/cmd/server/api" ) @@ -103,10 +104,9 @@ func (b BuildDefaults) applyPodDefaults(pod *kapi.Pod) { func (b BuildDefaults) applyBuildDefaults(build *buildapi.Build) { // Apply default env - buildEnv := getBuildEnv(build) for _, envVar := range b.config.Env { glog.V(5).Infof("Adding default environment variable %s=%s to build %s/%s", envVar.Name, envVar.Value, build.Namespace, build.Name) - addDefaultEnvVar(envVar, buildEnv) + addDefaultEnvVar(build, envVar) } // Apply default labels @@ -174,29 +174,16 @@ func (b BuildDefaults) applyBuildDefaults(build *buildapi.Build) { } } -func getBuildEnv(build *buildapi.Build) *[]kapi.EnvVar { - switch { - case build.Spec.Strategy.DockerStrategy != nil: - return &build.Spec.Strategy.DockerStrategy.Env - case build.Spec.Strategy.SourceStrategy != nil: - return &build.Spec.Strategy.SourceStrategy.Env - case build.Spec.Strategy.CustomStrategy != nil: - return &build.Spec.Strategy.CustomStrategy.Env - } - return nil -} - -func addDefaultEnvVar(v kapi.EnvVar, envVars *[]kapi.EnvVar) { - if envVars == nil { - return - } +func addDefaultEnvVar(build *buildapi.Build, v kapi.EnvVar) { + envVars := buildutil.GetBuildEnv(build) - for i := range *envVars { - if (*envVars)[i].Name == v.Name { + for i := range envVars { + if envVars[i].Name == v.Name { return } } - *envVars = append(*envVars, v) + envVars = append(envVars, v) + buildutil.SetBuildEnv(build, envVars) } func addDefaultLabel(defaultLabel buildapi.ImageLabel, buildLabels *[]buildapi.ImageLabel) { diff --git a/pkg/build/admission/defaults/admission_test.go b/pkg/build/admission/defaults/admission_test.go index 5c572afcd4b5..7225562690c8 100644 --- a/pkg/build/admission/defaults/admission_test.go +++ b/pkg/build/admission/defaults/admission_test.go @@ -11,6 +11,7 @@ import ( defaultsapi "github.com/openshift/origin/pkg/build/admission/defaults/api" u "github.com/openshift/origin/pkg/build/admission/testutil" buildapi "github.com/openshift/origin/pkg/build/api" + buildutil "github.com/openshift/origin/pkg/build/util" _ "github.com/openshift/origin/pkg/api/install" ) @@ -73,9 +74,9 @@ func TestEnvDefaults(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - env := getBuildEnv(build) + env := buildutil.GetBuildEnv(build) var1found, var2found := false, false - for _, ev := range *env { + for _, ev := range env { if ev.Name == "VAR1" { if ev.Value != "VALUE1" { t.Errorf("unexpected value %s", ev.Value) diff --git a/pkg/build/admission/defaults/api/validation/validation_test.go b/pkg/build/admission/defaults/api/validation/validation_test.go index 14aa81c2bb7d..5d0afe6067c1 100644 --- a/pkg/build/admission/defaults/api/validation/validation_test.go +++ b/pkg/build/admission/defaults/api/validation/validation_test.go @@ -83,19 +83,23 @@ func TestValidateBuildDefaultsConfig(t *testing.T) { errField: "env[0].name", errType: field.ErrorTypeInvalid, }, - // 5: valueFrom present in env var + // 5: ResourceFieldRef present in env var { config: &defaultsapi.BuildDefaultsConfig{ Env: []kapi.EnvVar{ { - Name: "name", - Value: "test", - ValueFrom: &kapi.EnvVarSource{}, + Name: "name", + ValueFrom: &kapi.EnvVarSource{ + ResourceFieldRef: &kapi.ResourceFieldSelector{ + ContainerName: "name", + Resource: "resource", + }, + }, }, }, }, errExpected: true, - errField: "env[0].valueFrom", + errField: "env[0].valueFrom.ResourceFieldRef", errType: field.ErrorTypeInvalid, }, // 6: label: empty name diff --git a/pkg/build/api/types.go b/pkg/build/api/types.go index 3f40b375aae1..972015024f22 100644 --- a/pkg/build/api/types.go +++ b/pkg/build/api/types.go @@ -483,6 +483,10 @@ const ( // one container with a non-zero exit status. StatusReasonFailedContainer StatusReason = "FailedContainer" + // StatusReasonUnresolvableEnvironmentVariable indicates that an error occurred processing + // the supplied options for environment variables in the build strategy environment + StatusReasonUnresolvableEnvironmentVariable StatusReason = "UnresolvableEnvironmentVariable" + // StatusReasonGenericBuildFailed is the reason associated with a broad // range of build failures. StatusReasonGenericBuildFailed StatusReason = "GenericBuildFailed" @@ -490,24 +494,25 @@ const ( // NOTE: These messages might change. const ( - StatusMessageCannotCreateBuildPodSpec = "Failed to create pod spec." - StatusMessageCannotCreateBuildPod = "Failed creating build pod." - StatusMessageInvalidOutputRef = "Output image could not be resolved." - StatusMessageCancelBuildFailed = "Failed to cancel build." - StatusMessageBuildPodDeleted = "The pod for this build was deleted before the build completed." - StatusMessageExceededRetryTimeout = "Build did not complete and retrying timed out." - StatusMessageMissingPushSecret = "Missing push secret." - StatusMessagePostCommitHookFailed = "Build failed because of post commit hook." - StatusMessagePushImageToRegistryFailed = "Failed to push the image to the registry." - StatusMessagePullBuilderImageFailed = "Failed pulling builder image." - StatusMessageFetchSourceFailed = "Failed to fetch the input source." - StatusMessageInvalidContextDirectory = "The supplied context directory does not exist." - StatusMessageCancelledBuild = "The build was cancelled by the user." - StatusMessageDockerBuildFailed = "Docker build strategy has failed." - StatusMessageBuildPodExists = "The pod for this build already exists and is older than the build." - StatusMessageNoBuildContainerStatus = "The pod for this build has no container statuses indicating success or failure." - StatusMessageFailedContainer = "The pod for this build has at least one container with a non-zero exit status." - StatusMessageGenericBuildFailed = "Generic Build failure - check logs for details." + StatusMessageCannotCreateBuildPodSpec = "Failed to create pod spec." + StatusMessageCannotCreateBuildPod = "Failed creating build pod." + StatusMessageInvalidOutputRef = "Output image could not be resolved." + StatusMessageCancelBuildFailed = "Failed to cancel build." + StatusMessageBuildPodDeleted = "The pod for this build was deleted before the build completed." + StatusMessageExceededRetryTimeout = "Build did not complete and retrying timed out." + StatusMessageMissingPushSecret = "Missing push secret." + StatusMessagePostCommitHookFailed = "Build failed because of post commit hook." + StatusMessagePushImageToRegistryFailed = "Failed to push the image to the registry." + StatusMessagePullBuilderImageFailed = "Failed pulling builder image." + StatusMessageFetchSourceFailed = "Failed to fetch the input source." + StatusMessageInvalidContextDirectory = "The supplied context directory does not exist." + StatusMessageCancelledBuild = "The build was cancelled by the user." + StatusMessageDockerBuildFailed = "Docker build strategy has failed." + StatusMessageBuildPodExists = "The pod for this build already exists and is older than the build." + StatusMessageNoBuildContainerStatus = "The pod for this build has no container statuses indicating success or failure." + StatusMessageFailedContainer = "The pod for this build has at least one container with a non-zero exit status." + StatusMessageGenericBuildFailed = "Generic Build failure - check logs for details." + StatusMessageUnresolvableEnvironmentVariable = "Unable to resolve build environment variable reference." ) // BuildStatusOutput contains the status of the built image. @@ -720,7 +725,6 @@ type CustomBuildStrategy struct { PullSecret *kapi.LocalObjectReference // Env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar // ExposeDockerSocket will allow running Docker commands (and build Docker images) from @@ -777,7 +781,6 @@ type DockerBuildStrategy struct { NoCache bool // Env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar // Args contains any build arguments that are to be passed to Docker. See @@ -813,7 +816,6 @@ type SourceBuildStrategy struct { PullSecret *kapi.LocalObjectReference // Env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar // Scripts is the location of Source scripts @@ -852,7 +854,6 @@ type JenkinsPipelineBuildStrategy struct { Jenkinsfile string // Env contains additional environment variables you want to pass into a build pipeline. - // ValueFrom is not supported. Env []kapi.EnvVar } @@ -1209,7 +1210,6 @@ type BuildRequest struct { LastVersion *int64 // Env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar // TriggeredBy describes which triggers started the most recent update to the diff --git a/pkg/build/api/v1/generated.proto b/pkg/build/api/v1/generated.proto index d86c2cf44798..f29b23a0e2ad 100644 --- a/pkg/build/api/v1/generated.proto +++ b/pkg/build/api/v1/generated.proto @@ -319,7 +319,6 @@ message BuildRequest { optional int64 lastVersion = 6; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 7; // triggeredBy describes which triggers started the most recent update to the @@ -586,7 +585,6 @@ message CustomBuildStrategy { optional k8s.io.kubernetes.pkg.api.v1.LocalObjectReference pullSecret = 2; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 3; // exposeDockerSocket will allow running Docker commands (and build Docker images) from @@ -622,7 +620,6 @@ message DockerBuildStrategy { optional bool noCache = 3; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 4; // forcePull describes if the builder should pull the images from registry prior to building. @@ -808,7 +805,6 @@ message JenkinsPipelineBuildStrategy { optional string jenkinsfile = 2; // env contains additional environment variables you want to pass into a build pipeline. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 3; } @@ -873,7 +869,6 @@ message SourceBuildStrategy { optional k8s.io.kubernetes.pkg.api.v1.LocalObjectReference pullSecret = 2; // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. repeated k8s.io.kubernetes.pkg.api.v1.EnvVar env = 3; // scripts is the location of Source scripts diff --git a/pkg/build/api/v1/swagger_doc.go b/pkg/build/api/v1/swagger_doc.go index b3f686ddcc7b..d0e00f6392c8 100644 --- a/pkg/build/api/v1/swagger_doc.go +++ b/pkg/build/api/v1/swagger_doc.go @@ -157,7 +157,7 @@ var map_BuildRequest = map[string]string{ "from": "from is the reference to the ImageStreamTag that triggered the build.", "binary": "binary indicates a request to build from a binary provided to the builder", "lastVersion": "lastVersion (optional) is the LastVersion of the BuildConfig that was used to generate the build. If the BuildConfig in the generator doesn't match, a build will not be generated.", - "env": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "env": "env contains additional environment variables you want to pass into a builder container.", "triggeredBy": "triggeredBy describes which triggers started the most recent update to the build configuration and contains information about those triggers.", "dockerStrategyOptions": "DockerStrategyOptions contains additional docker-strategy specific options for the build", } @@ -300,7 +300,7 @@ var map_CustomBuildStrategy = map[string]string{ "": "CustomBuildStrategy defines input parameters specific to Custom build.", "from": "from is reference to an DockerImage, ImageStreamTag, or ImageStreamImage from which the docker image should be pulled", "pullSecret": "pullSecret is the name of a Secret that would be used for setting up the authentication for pulling the Docker images from the private Docker registries", - "env": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "env": "env contains additional environment variables you want to pass into a builder container.", "exposeDockerSocket": "exposeDockerSocket will allow running Docker commands (and build Docker images) from inside the Docker container.", "forcePull": "forcePull describes if the controller should configure the build pod to always pull the images for the builder or only pull if it is not present locally", "secrets": "secrets is a list of additional secrets that will be included in the build pod", @@ -316,7 +316,7 @@ var map_DockerBuildStrategy = map[string]string{ "from": "from is reference to an DockerImage, ImageStreamTag, or ImageStreamImage from which the docker image should be pulled the resulting image will be used in the FROM line of the Dockerfile for this build.", "pullSecret": "pullSecret is the name of a Secret that would be used for setting up the authentication for pulling the Docker images from the private Docker registries", "noCache": "noCache if set to true indicates that the docker build must be executed with the --no-cache=true flag", - "env": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "env": "env contains additional environment variables you want to pass into a builder container.", "forcePull": "forcePull describes if the builder should pull the images from registry prior to building.", "dockerfilePath": "dockerfilePath is the path of the Dockerfile that will be used to build the Docker image, relative to the root of the context (contextDir).", "buildArgs": "buildArgs contains build arguments that will be resolved in the Dockerfile. See https://docs.docker.com/engine/reference/builder/#/arg for more details.", @@ -461,7 +461,7 @@ var map_JenkinsPipelineBuildStrategy = map[string]string{ "": "JenkinsPipelineBuildStrategy holds parameters specific to a Jenkins Pipeline build. This strategy is in tech preview.", "jenkinsfilePath": "JenkinsfilePath is the optional path of the Jenkinsfile that will be used to configure the pipeline relative to the root of the context (contextDir). If both JenkinsfilePath & Jenkinsfile are both not specified, this defaults to Jenkinsfile in the root of the specified contextDir.", "jenkinsfile": "Jenkinsfile defines the optional raw contents of a Jenkinsfile which defines a Jenkins pipeline build.", - "env": "env contains additional environment variables you want to pass into a build pipeline. ValueFrom is not supported.", + "env": "env contains additional environment variables you want to pass into a build pipeline.", } func (JenkinsPipelineBuildStrategy) SwaggerDoc() map[string]string { @@ -503,7 +503,7 @@ var map_SourceBuildStrategy = map[string]string{ "": "SourceBuildStrategy defines input parameters specific to an Source build.", "from": "from is reference to an DockerImage, ImageStreamTag, or ImageStreamImage from which the docker image should be pulled", "pullSecret": "pullSecret is the name of a Secret that would be used for setting up the authentication for pulling the Docker images from the private Docker registries", - "env": "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + "env": "env contains additional environment variables you want to pass into a builder container.", "scripts": "scripts is the location of Source scripts", "incremental": "incremental flag forces the Source build to do incremental builds if true.", "forcePull": "forcePull describes if the builder should pull the images from registry prior to building.", diff --git a/pkg/build/api/v1/types.go b/pkg/build/api/v1/types.go index a7137f4c5579..792f5ccb8afd 100644 --- a/pkg/build/api/v1/types.go +++ b/pkg/build/api/v1/types.go @@ -584,7 +584,6 @@ type CustomBuildStrategy struct { PullSecret *kapi.LocalObjectReference `json:"pullSecret,omitempty" protobuf:"bytes,2,opt,name=pullSecret"` // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,3,rep,name=env"` // exposeDockerSocket will allow running Docker commands (and build Docker images) from @@ -641,7 +640,6 @@ type DockerBuildStrategy struct { NoCache bool `json:"noCache,omitempty" protobuf:"varint,3,opt,name=noCache"` // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,4,rep,name=env"` // forcePull describes if the builder should pull the images from registry prior to building. @@ -677,7 +675,6 @@ type SourceBuildStrategy struct { PullSecret *kapi.LocalObjectReference `json:"pullSecret,omitempty" protobuf:"bytes,2,opt,name=pullSecret"` // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,3,rep,name=env"` // scripts is the location of Source scripts @@ -718,7 +715,6 @@ type JenkinsPipelineBuildStrategy struct { Jenkinsfile string `json:"jenkinsfile,omitempty" protobuf:"bytes,2,opt,name=jenkinsfile"` // env contains additional environment variables you want to pass into a build pipeline. - // ValueFrom is not supported. Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,3,rep,name=env"` } @@ -1062,7 +1058,6 @@ type BuildRequest struct { LastVersion *int64 `json:"lastVersion,omitempty" protobuf:"varint,6,opt,name=lastVersion"` // env contains additional environment variables you want to pass into a builder container. - // ValueFrom is not supported. Env []kapi.EnvVar `json:"env,omitempty" protobuf:"bytes,7,rep,name=env"` // triggeredBy describes which triggers started the most recent update to the diff --git a/pkg/build/api/validation/validation.go b/pkg/build/api/validation/validation.go index e26b93f8f6c6..6c940f747e68 100644 --- a/pkg/build/api/validation/validation.go +++ b/pkg/build/api/validation/validation.go @@ -662,8 +662,8 @@ func ValidateStrategyEnv(vars []kapi.EnvVar, fldPath *field.Path) field.ErrorLis } else if len(kvalidation.IsCIdentifier(ev.Name)) > 0 { allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, cIdentifierErrorMsg)) } - if ev.ValueFrom != nil { - allErrs = append(allErrs, field.Invalid(idxPath.Child("valueFrom"), ev.ValueFrom, "valueFrom is not supported in build strategy environment variables")) + if ev.ValueFrom != nil && ev.ValueFrom.ResourceFieldRef != nil { + allErrs = append(allErrs, field.Invalid(idxPath.Child("valueFrom").Child("ResourceFieldRef"), ev.Name, "ResourceFieldRef is not valid for valueFrom in build environment variables")) } } return allErrs diff --git a/pkg/build/api/validation/validation_test.go b/pkg/build/api/validation/validation_test.go index 57a0462b88ec..cb6cea170309 100644 --- a/pkg/build/api/validation/validation_test.go +++ b/pkg/build/api/validation/validation_test.go @@ -1873,29 +1873,6 @@ func TestValidateCommonSpec(t *testing.T) { }, }, // 32 - // jenkins strategy env fields can't use valueFrom syntax - { - string(field.ErrorTypeInvalid) + "strategy.jenkinsPipelineStrategy.env[0].valueFrom", - buildapi.CommonSpec{ - Source: buildapi.BuildSource{ - Git: &buildapi.GitBuildSource{ - URI: "http://github.com/my/repository", - }, - }, - Strategy: buildapi.BuildStrategy{ - JenkinsPipelineStrategy: &buildapi.JenkinsPipelineBuildStrategy{ - JenkinsfilePath: "myJenkinsfile", - Env: []kapi.EnvVar{ - { - Name: "key", - ValueFrom: &kapi.EnvVarSource{}, - }, - }, - }, - }, - }, - }, - // 33 { string(field.ErrorTypeRequired) + "output.imageLabels[0].name", buildapi.CommonSpec{ @@ -1917,7 +1894,7 @@ func TestValidateCommonSpec(t *testing.T) { }, }, }, - // 34 + // 33 { string(field.ErrorTypeInvalid) + "output.imageLabels[0].name", buildapi.CommonSpec{ @@ -1939,7 +1916,7 @@ func TestValidateCommonSpec(t *testing.T) { }, }, }, - // 35 + // 34 // duplicate labels { string(field.ErrorTypeInvalid) + "output.imageLabels[1].name", @@ -1966,7 +1943,7 @@ func TestValidateCommonSpec(t *testing.T) { }, }, }, - // 36 + // 35 // nonconsecutive duplicate labels { string(field.ErrorTypeInvalid) + "output.imageLabels[3].name", @@ -2005,7 +1982,7 @@ func TestValidateCommonSpec(t *testing.T) { }, }, }, - // 37 + // 36 // invalid nodeselector { string(field.ErrorTypeInvalid) + "nodeSelector[A@B!]", @@ -2687,20 +2664,7 @@ func TestValidateStrategyEnvVars(t *testing.T) { errField: "env[0].name", errType: field.ErrorTypeInvalid, }, - // 2: valueFrom present in env var - { - env: []kapi.EnvVar{ - { - Name: "name", - Value: "test", - ValueFrom: &kapi.EnvVarSource{}, - }, - }, - errExpected: true, - errField: "env[0].valueFrom", - errType: field.ErrorTypeInvalid, - }, - // 3: valid env + // 2: valid env { env: []kapi.EnvVar{ { diff --git a/pkg/build/controller/build/build_controller.go b/pkg/build/controller/build/build_controller.go index 12e686b4c103..6cc8e7324bf5 100644 --- a/pkg/build/controller/build/build_controller.go +++ b/pkg/build/controller/build/build_controller.go @@ -52,6 +52,7 @@ type BuildController struct { buildConfigGetter buildclient.BuildConfigGetter buildDeleter buildclient.BuildDeleter podClient kcoreclient.PodsGetter + kubeClient kclientset.Interface queue workqueue.RateLimitingInterface @@ -113,6 +114,7 @@ func NewBuildController(params *BuildControllerParams) *BuildController { buildDeleter: buildClient, secretStore: params.SecretInformer.Lister(), podClient: params.KubeClientInternal.Core(), + kubeClient: params.KubeClientInternal, podInformer: params.PodInformer.Informer(), podStore: params.PodInformer.Lister(), buildInformer: params.BuildInformer.Informer(), @@ -358,6 +360,11 @@ func (bc *BuildController) createPodSpec(originalBuild *buildapi.Build, ref stri if err := bc.buildOverrides.ApplyOverrides(podSpec); err != nil { return nil, fmt.Errorf("failed to apply build overrides for build %s/%s: %v", build.Namespace, build.Name, err) } + + // Handle resolving ValueFrom references in build environment variables + if err := common.ResolveValueFrom(podSpec, bc.kubeClient); err != nil { + return nil, err + } return podSpec, nil } @@ -425,12 +432,18 @@ func (bc *BuildController) createBuildPod(build *buildapi.Build) (*buildUpdate, // Create the build pod spec buildPod, err := bc.createPodSpec(build, ref) if err != nil { + switch err.(type) { + case common.ErrEnvVarResolver: + update = transitionToPhase(buildapi.BuildPhaseError, buildapi.StatusReasonUnresolvableEnvironmentVariable, fmt.Sprintf("%v, %v", buildapi.StatusMessageUnresolvableEnvironmentVariable, err.Error())) + default: + update.setReason(buildapi.StatusReasonCannotCreateBuildPodSpec) + update.setMessage(buildapi.StatusMessageCannotCreateBuildPodSpec) + + } // If an error occurred when creating the pod spec, it likely means // that the build is something we don't understand. For example, it could // have a strategy that we don't recognize. It will remain in New state // and be updated with the reason that it is still in New - update.setReason(buildapi.StatusReasonCannotCreateBuildPodSpec) - update.setMessage(buildapi.StatusMessageCannotCreateBuildPodSpec) // The error will be logged, but will not be returned to the caller // to be retried. The reason is that there's really no external factor diff --git a/pkg/build/controller/common/util.go b/pkg/build/controller/common/util.go index 1c0a62d13d3f..5360010ae58c 100644 --- a/pkg/build/controller/common/util.go +++ b/pkg/build/controller/common/util.go @@ -6,12 +6,18 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kerrors "k8s.io/apimachinery/pkg/util/errors" + utilerrors "k8s.io/apimachinery/pkg/util/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + kapi "k8s.io/kubernetes/pkg/api" + kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/third_party/forked/golang/expansion" + buildadmission "github.com/openshift/origin/pkg/build/admission" buildapi "github.com/openshift/origin/pkg/build/api" buildclient "github.com/openshift/origin/pkg/build/client" "github.com/openshift/origin/pkg/build/controller/policy" buildutil "github.com/openshift/origin/pkg/build/util" + envutil "github.com/openshift/origin/pkg/util/env" "github.com/golang/glog" ) @@ -136,3 +142,57 @@ func HasBuildPodNameAnnotation(build *buildapi.Build) bool { _, hasAnnotation := build.Annotations[buildapi.BuildPodNameAnnotation] return hasAnnotation } + +// ErrEnvVarResolver is an error type for build environment resolution errors +type ErrEnvVarResolver struct { + message kerrors.Aggregate +} + +// Error returns a string representation of the error +func (e ErrEnvVarResolver) Error() string { + return fmt.Sprintf("%v", e.message) +} + +// ResolveValueFrom resolves valueFrom references in build environment variables +// including references to existing environment variables, jsonpath references to +// the build object, secrets, and configmaps. +// The build.Strategy.BuildStrategy.Env is replaced with the resolved references. +func ResolveValueFrom(pod *kapi.Pod, client kclientset.Interface) error { + var outputEnv []kapi.EnvVar + var allErrs []error + + build, version, err := buildadmission.GetBuildFromPod(pod) + if err != nil { + return nil + } + + mapEnvs := map[string]string{} + mapping := expansion.MappingFuncFor(mapEnvs) + inputEnv := buildutil.GetBuildEnv(build) + store := envutil.NewResourceStore() + + for _, e := range inputEnv { + var value string + var err error + + if e.Value != "" { + value = expansion.Expand(e.Value, mapping) + } else if e.ValueFrom != nil { + value, err = envutil.GetEnvVarRefValue(nil, client, build.Namespace, store, e.ValueFrom, build, nil) + if err != nil { + allErrs = append(allErrs, err) + continue + } + } + + outputEnv = append(outputEnv, kapi.EnvVar{Name: e.Name, Value: value}) + mapEnvs[e.Name] = value + } + + if len(allErrs) > 0 { + return ErrEnvVarResolver{utilerrors.NewAggregate(allErrs)} + } + + buildutil.SetBuildEnv(build, outputEnv) + return buildadmission.SetBuildInPod(pod, build, version) +} diff --git a/pkg/build/generator/generator.go b/pkg/build/generator/generator.go index bca17e185463..70c0be1e6689 100644 --- a/pkg/build/generator/generator.go +++ b/pkg/build/generator/generator.go @@ -193,39 +193,6 @@ func describeBuildRequest(request *buildapi.BuildRequest) string { return desc } -// updateBuildEnv updates the strategy environment -// This will replace the existing variable definitions with provided env -func updateBuildEnv(strategy *buildapi.BuildStrategy, env []kapi.EnvVar) { - var buildEnv *[]kapi.EnvVar - - switch { - case strategy.SourceStrategy != nil: - buildEnv = &strategy.SourceStrategy.Env - case strategy.DockerStrategy != nil: - buildEnv = &strategy.DockerStrategy.Env - case strategy.CustomStrategy != nil: - buildEnv = &strategy.CustomStrategy.Env - case strategy.JenkinsPipelineStrategy != nil: - buildEnv = &strategy.JenkinsPipelineStrategy.Env - } - - newEnv := []kapi.EnvVar{} - for _, e := range *buildEnv { - exists := false - for _, n := range env { - if e.Name == n.Name { - exists = true - break - } - } - if !exists { - newEnv = append(newEnv, e) - } - } - newEnv = append(newEnv, env...) - *buildEnv = newEnv -} - // Adds new Build Args to existing Build Args. Overwrites existing ones func updateBuildArgs(oldArgs *[]kapi.EnvVar, newArgs []kapi.EnvVar) []kapi.EnvVar { combined := make(map[string]string) @@ -304,7 +271,7 @@ func (g *BuildGenerator) instantiate(ctx apirequest.Context, request *buildapi.B newBuild.Spec.TriggeredBy = request.TriggeredBy if len(request.Env) > 0 { - updateBuildEnv(&newBuild.Spec.Strategy, request.Env) + buildutil.UpdateBuildEnv(newBuild, request.Env) } // Update the Docker build args @@ -431,7 +398,7 @@ func (g *BuildGenerator) clone(ctx apirequest.Context, request *buildapi.BuildRe newBuild.Spec.TriggeredBy = request.TriggeredBy if len(request.Env) > 0 { - updateBuildEnv(&newBuild.Spec.Strategy, request.Env) + buildutil.UpdateBuildEnv(newBuild, request.Env) } // Update the Docker build args diff --git a/pkg/build/util/util.go b/pkg/build/util/util.go index da5907feebc1..c559af4322cd 100644 --- a/pkg/build/util/util.go +++ b/pkg/build/util/util.go @@ -318,3 +318,59 @@ func SafeForLoggingS2IConfig(config *s2iapi.Config) *s2iapi.Config { newConfig.ScriptsURL, _ = s2iutil.SafeForLoggingURL(newConfig.ScriptsURL) return &newConfig } + +// GetBuildEnv gets the build strategy environment +func GetBuildEnv(build *buildapi.Build) []kapi.EnvVar { + switch { + case build.Spec.Strategy.SourceStrategy != nil: + return build.Spec.Strategy.SourceStrategy.Env + case build.Spec.Strategy.DockerStrategy != nil: + return build.Spec.Strategy.DockerStrategy.Env + case build.Spec.Strategy.CustomStrategy != nil: + return build.Spec.Strategy.CustomStrategy.Env + case build.Spec.Strategy.JenkinsPipelineStrategy != nil: + return build.Spec.Strategy.JenkinsPipelineStrategy.Env + default: + return nil + } +} + +// SetBuildEnv replaces the current build environment +func SetBuildEnv(build *buildapi.Build, env []kapi.EnvVar) { + var oldEnv *[]kapi.EnvVar + + switch { + case build.Spec.Strategy.SourceStrategy != nil: + oldEnv = &build.Spec.Strategy.SourceStrategy.Env + case build.Spec.Strategy.DockerStrategy != nil: + oldEnv = &build.Spec.Strategy.DockerStrategy.Env + case build.Spec.Strategy.CustomStrategy != nil: + oldEnv = &build.Spec.Strategy.CustomStrategy.Env + case build.Spec.Strategy.JenkinsPipelineStrategy != nil: + oldEnv = &build.Spec.Strategy.JenkinsPipelineStrategy.Env + } + *oldEnv = env + +} + +// UpdateBuildEnv updates the strategy environment +// This will replace the existing variable definitions with provided env +func UpdateBuildEnv(build *buildapi.Build, env []kapi.EnvVar) { + buildEnv := GetBuildEnv(build) + + newEnv := []kapi.EnvVar{} + for _, e := range buildEnv { + exists := false + for _, n := range env { + if e.Name == n.Name { + exists = true + break + } + } + if !exists { + newEnv = append(newEnv, e) + } + } + newEnv = append(newEnv, env...) + SetBuildEnv(build, newEnv) +} diff --git a/pkg/cmd/cli/cmd/set/env.go b/pkg/cmd/cli/cmd/set/env.go index ec3ee9552ada..425cd3d77615 100644 --- a/pkg/cmd/cli/cmd/set/env.go +++ b/pkg/cmd/cli/cmd/set/env.go @@ -12,18 +12,17 @@ import ( "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" kapi "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/fieldpath" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "github.com/openshift/origin/pkg/cmd/templates" cmdutil "github.com/openshift/origin/pkg/cmd/util" "github.com/openshift/origin/pkg/cmd/util/clientcmd" + envutil "github.com/openshift/origin/pkg/util/env" ) var ( @@ -167,108 +166,6 @@ func keyToEnvName(key string) string { return strings.ToUpper(validEnvNameRegexp.ReplaceAllString(key, "_")) } -type resourceStore struct { - secretStore map[string]*kapi.Secret - configMapStore map[string]*kapi.ConfigMap -} - -func newResourceStore() *resourceStore { - return &resourceStore{ - secretStore: make(map[string]*kapi.Secret), - configMapStore: make(map[string]*kapi.ConfigMap), - } -} - -func getSecretRefValue(f *clientcmd.Factory, store *resourceStore, secretSelector *kapi.SecretKeySelector) (string, error) { - secret, ok := store.secretStore[secretSelector.Name] - if !ok { - kubeClient, err := f.ClientSet() - if err != nil { - return "", err - } - namespace, _, err := f.DefaultNamespace() - if err != nil { - return "", err - } - secret, err = kubeClient.Core().Secrets(namespace).Get(secretSelector.Name, metav1.GetOptions{}) - if err != nil { - return "", err - } - store.secretStore[secretSelector.Name] = secret - } - if data, ok := secret.Data[secretSelector.Key]; ok { - return string(data), nil - } - return "", fmt.Errorf("key %s not found in secret %s", secretSelector.Key, secretSelector.Name) -} - -func getConfigMapRefValue(f *clientcmd.Factory, store *resourceStore, configMapSelector *kapi.ConfigMapKeySelector) (string, error) { - configMap, ok := store.configMapStore[configMapSelector.Name] - if !ok { - kubeClient, err := f.ClientSet() - if err != nil { - return "", err - } - namespace, _, err := f.DefaultNamespace() - if err != nil { - return "", err - } - configMap, err = kubeClient.Core().ConfigMaps(namespace).Get(configMapSelector.Name, metav1.GetOptions{}) - if err != nil { - return "", err - } - store.configMapStore[configMapSelector.Name] = configMap - } - if data, ok := configMap.Data[configMapSelector.Key]; ok { - return string(data), nil - } - return "", fmt.Errorf("key %s not found in config map %s", configMapSelector.Key, configMapSelector.Name) -} - -func getEnvVarRefValue(f *clientcmd.Factory, store *resourceStore, from *kapi.EnvVarSource, obj runtime.Object, c *kapi.Container) (string, error) { - if from.SecretKeyRef != nil { - return getSecretRefValue(f, store, from.SecretKeyRef) - } - - if from.ConfigMapKeyRef != nil { - return getConfigMapRefValue(f, store, from.ConfigMapKeyRef) - } - - if from.FieldRef != nil { - return fieldpath.ExtractFieldPathAsString(obj, from.FieldRef.FieldPath) - } - - if from.ResourceFieldRef != nil { - return fieldpath.InternalExtractContainerResourceValue(from.ResourceFieldRef, c) - } - - return "", fmt.Errorf("invalid valueFrom") -} - -func getEnvVarRefString(from *kapi.EnvVarSource) string { - if from.ConfigMapKeyRef != nil { - return fmt.Sprintf("configmap %s, key %s", from.ConfigMapKeyRef.Name, from.ConfigMapKeyRef.Key) - } - - if from.SecretKeyRef != nil { - return fmt.Sprintf("secret %s, key %s", from.SecretKeyRef.Name, from.SecretKeyRef.Key) - } - - if from.FieldRef != nil { - return fmt.Sprintf("field path %s", from.FieldRef.FieldPath) - } - - if from.ResourceFieldRef != nil { - containerPrefix := "" - if from.ResourceFieldRef.ContainerName != "" { - containerPrefix = fmt.Sprintf("%s/", from.ResourceFieldRef.ContainerName) - } - return fmt.Sprintf("resource field %s%s", containerPrefix, from.ResourceFieldRef.Resource) - } - - return "invalid valueFrom" -} - func (o *EnvOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string) error { resources, envArgs, ok := cmdutil.SplitEnvironmentFromResources(args) if !ok { @@ -450,7 +347,7 @@ func (o *EnvOptions) RunEnv(f *clientcmd.Factory) error { if o.List { resolveErrors := map[string][]string{} - store := newResourceStore() + store := envutil.NewResourceStore() fmt.Fprintf(o.Out, "# %s %s, container %s\n", info.Mapping.Resource, info.Name, c.Name) for _, env := range c.Env { @@ -462,11 +359,11 @@ func (o *EnvOptions) RunEnv(f *clientcmd.Factory) error { // Print the reference version if !o.Resolve { - fmt.Fprintf(o.Out, "# %s from %s\n", env.Name, getEnvVarRefString(env.ValueFrom)) + fmt.Fprintf(o.Out, "# %s from %s\n", env.Name, envutil.GetEnvVarRefString(env.ValueFrom)) continue } - value, err := getEnvVarRefValue(f, store, env.ValueFrom, info.Object, c) + value, err := envutil.GetEnvVarRefValue(f, nil, "", store, env.ValueFrom, info.Object, c) // Print the resolved value if err == nil { fmt.Fprintf(o.Out, "%s=%s\n", env.Name, value) @@ -474,7 +371,7 @@ func (o *EnvOptions) RunEnv(f *clientcmd.Factory) error { } // Print the reference version and save the resolve error - fmt.Fprintf(o.Out, "# %s from %s\n", env.Name, getEnvVarRefString(env.ValueFrom)) + fmt.Fprintf(o.Out, "# %s from %s\n", env.Name, envutil.GetEnvVarRefString(env.ValueFrom)) errString := err.Error() resolveErrors[errString] = append(resolveErrors[errString], env.Name) resolutionErrorsEncountered = true diff --git a/pkg/cmd/server/bootstrappolicy/controller_policy.go b/pkg/cmd/server/bootstrappolicy/controller_policy.go index 24c772a56862..4c7a65d56be3 100644 --- a/pkg/cmd/server/bootstrappolicy/controller_policy.go +++ b/pkg/cmd/server/bootstrappolicy/controller_policy.go @@ -54,6 +54,7 @@ func init() { rbac.NewRule("create").Groups(buildGroup, legacyBuildGroup).Resources("builds/optimizeddocker", "builds/docker", "builds/source", "builds/custom", "builds/jenkinspipeline").RuleOrDie(), rbac.NewRule("get", "list").Groups(imageGroup, legacyImageGroup).Resources("imagestreams").RuleOrDie(), rbac.NewRule("get", "list").Groups(kapiGroup).Resources("secrets").RuleOrDie(), + rbac.NewRule("get", "list").Groups(kapiGroup).Resources("configmaps").RuleOrDie(), rbac.NewRule("get", "list", "create", "delete").Groups(kapiGroup).Resources("pods").RuleOrDie(), rbac.NewRule("get").Groups(kapiGroup).Resources("namespaces").RuleOrDie(), eventsRule(), diff --git a/pkg/openapi/zz_generated.openapi.go b/pkg/openapi/zz_generated.openapi.go index 91ee2a951eea..825ed9408c6f 100644 --- a/pkg/openapi/zz_generated.openapi.go +++ b/pkg/openapi/zz_generated.openapi.go @@ -2797,7 +2797,7 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope }, "env": { SchemaProps: spec.SchemaProps{ - Description: "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + Description: "env contains additional environment variables you want to pass into a builder container.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3384,7 +3384,7 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope }, "env": { SchemaProps: spec.SchemaProps{ - Description: "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + Description: "env contains additional environment variables you want to pass into a builder container.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3462,7 +3462,7 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope }, "env": { SchemaProps: spec.SchemaProps{ - Description: "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + Description: "env contains additional environment variables you want to pass into a builder container.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -3964,7 +3964,7 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope }, "env": { SchemaProps: spec.SchemaProps{ - Description: "env contains additional environment variables you want to pass into a build pipeline. ValueFrom is not supported.", + Description: "env contains additional environment variables you want to pass into a build pipeline.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -4081,7 +4081,7 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope }, "env": { SchemaProps: spec.SchemaProps{ - Description: "env contains additional environment variables you want to pass into a builder container. ValueFrom is not supported.", + Description: "env contains additional environment variables you want to pass into a builder container.", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ diff --git a/pkg/util/env/env.go b/pkg/util/env/env.go new file mode 100644 index 000000000000..e1139f4d836a --- /dev/null +++ b/pkg/util/env/env.go @@ -0,0 +1,138 @@ +package env + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + kapi "k8s.io/kubernetes/pkg/api" + kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/fieldpath" + + "github.com/openshift/origin/pkg/cmd/util/clientcmd" +) + +// ResourceStore defines a new resource store data structure +type ResourceStore struct { + SecretStore map[string]*kapi.Secret + ConfigMapStore map[string]*kapi.ConfigMap +} + +// NewResourceStore returns a pointer to a new resource store data structure +func NewResourceStore() *ResourceStore { + return &ResourceStore{ + SecretStore: make(map[string]*kapi.Secret), + ConfigMapStore: make(map[string]*kapi.ConfigMap), + } +} + +// getSecretRefValue returns the value of a secret in the supplied namespace +func getSecretRefValue(client kclientset.Interface, namespace string, store *ResourceStore, secretSelector *kapi.SecretKeySelector) (string, error) { + secret, ok := store.SecretStore[secretSelector.Name] + if !ok { + var err error + secret, err = client.Core().Secrets(namespace).Get(secretSelector.Name, metav1.GetOptions{}) + if err != nil { + return "", err + } + store.SecretStore[secretSelector.Name] = secret + } + if data, ok := secret.Data[secretSelector.Key]; ok { + return string(data), nil + } + return "", fmt.Errorf("key %s not found in secret %s", secretSelector.Key, secretSelector.Name) + +} + +// getConfigMapRefValue returns the value of a configmap in the supplied namespace +func getConfigMapRefValue(client kclientset.Interface, namespace string, store *ResourceStore, configMapSelector *kapi.ConfigMapKeySelector) (string, error) { + configMap, ok := store.ConfigMapStore[configMapSelector.Name] + if !ok { + var err error + configMap, err = client.Core().ConfigMaps(namespace).Get(configMapSelector.Name, metav1.GetOptions{}) + if err != nil { + return "", err + } + store.ConfigMapStore[configMapSelector.Name] = configMap + } + if data, ok := configMap.Data[configMapSelector.Key]; ok { + return string(data), nil + } + return "", fmt.Errorf("key %s not found in config map %s", configMapSelector.Key, configMapSelector.Name) +} + +// getFieldRef returns the value of the supplied path in the given object +func getFieldRef(obj runtime.Object, from *kapi.EnvVarSource) (string, error) { + return fieldpath.ExtractFieldPathAsString(obj, from.FieldRef.FieldPath) +} + +// getResourceFieldRef returns the value of a resource in the given container +func getResourceFieldRef(from *kapi.EnvVarSource, c *kapi.Container) (string, error) { + return fieldpath.InternalExtractContainerResourceValue(from.ResourceFieldRef, c) +} + +// GenEnvVarRefValue returns the value referenced by the supplied EnvVarSource given the other supplied information +func GetEnvVarRefValue(f *clientcmd.Factory, kc kclientset.Interface, ns string, store *ResourceStore, from *kapi.EnvVarSource, obj runtime.Object, c *kapi.Container) (string, error) { + var kubeClient kclientset.Interface + var namespace string + var err error + + if f != nil { + kubeClient, err = f.ClientSet() + if err != nil { + return "", err + } + namespace, _, err = f.DefaultNamespace() + if err != nil { + return "", err + } + } else if kc != nil { + kubeClient = kc + namespace = ns + } else { + return "", fmt.Errorf("You must supply either a clientcmd.Factory or kclientset.Interface") + } + + if from.SecretKeyRef != nil { + return getSecretRefValue(kubeClient, namespace, store, from.SecretKeyRef) + } + + if from.ConfigMapKeyRef != nil { + return getConfigMapRefValue(kubeClient, namespace, store, from.ConfigMapKeyRef) + } + + if from.FieldRef != nil { + return getFieldRef(obj, from) + } + + if from.ResourceFieldRef != nil { + return getResourceFieldRef(from, c) + } + + return "", fmt.Errorf("invalid valueFrom") +} + +// GenEnvVarRefString returns a text description of the supplied EnvVarSource +func GetEnvVarRefString(from *kapi.EnvVarSource) string { + if from.ConfigMapKeyRef != nil { + return fmt.Sprintf("configmap %s, key %s", from.ConfigMapKeyRef.Name, from.ConfigMapKeyRef.Key) + } + + if from.SecretKeyRef != nil { + return fmt.Sprintf("secret %s, key %s", from.SecretKeyRef.Name, from.SecretKeyRef.Key) + } + + if from.FieldRef != nil { + return fmt.Sprintf("field path %s", from.FieldRef.FieldPath) + } + + if from.ResourceFieldRef != nil { + containerPrefix := "" + if from.ResourceFieldRef.ContainerName != "" { + containerPrefix = fmt.Sprintf("%s/", from.ResourceFieldRef.ContainerName) + } + return fmt.Sprintf("resource field %s%s", containerPrefix, from.ResourceFieldRef.Resource) + } + + return "invalid valueFrom" +} diff --git a/test/extended/builds/valuefrom.go b/test/extended/builds/valuefrom.go new file mode 100644 index 000000000000..eb62952a7190 --- /dev/null +++ b/test/extended/builds/valuefrom.go @@ -0,0 +1,129 @@ +package builds + +import ( + "path/filepath" + "strings" + + g "github.com/onsi/ginkgo" + o "github.com/onsi/gomega" + + exutil "github.com/openshift/origin/test/extended/util" + + "k8s.io/kubernetes/test/e2e/framework" +) + +var _ = g.Describe("[builds][Conformance][valueFrom] process valueFrom in build strategy environment variables", func() { + var ( + valueFromBaseDir = exutil.FixturePath("testdata", "valuefrom") + testImageStreamFixture = filepath.Join(valueFromBaseDir, "test-is.json") + secretFixture = filepath.Join(valueFromBaseDir, "test-secret.yaml") + configmapFixture = filepath.Join(valueFromBaseDir, "test-configmap.yaml") + successfulSTIBuildValueFrom = filepath.Join(valueFromBaseDir, "successful-sti-build-value-from-config.yaml") + successfulDockerBuildValueFrom = filepath.Join(valueFromBaseDir, "successful-docker-build-value-from-config.yaml") + failedSTIBuildValueFrom = filepath.Join(valueFromBaseDir, "failed-sti-build-value-from-config.yaml") + failedDockerBuildValueFrom = filepath.Join(valueFromBaseDir, "failed-docker-build-value-from-config.yaml") + oc = exutil.NewCLI("build-valuefrom", exutil.KubeConfigPath()) + ) + + g.JustBeforeEach(func() { + g.By("waiting for builder service account") + err := exutil.WaitForBuilderAccount(oc.KubeClient().Core().ServiceAccounts(oc.Namespace())) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("waiting for openshift namespace imagestreams") + err = exutil.WaitForOpenShiftNamespaceImageStreams(oc) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("creating test image stream") + err = oc.Run("create").Args("-f", testImageStreamFixture).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("creating test secret") + err = oc.Run("create").Args("-f", secretFixture).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("creating test configmap") + err = oc.Run("create").Args("-f", configmapFixture).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + }) + + g.It("should successfully resolve valueFrom in s2i build environment variables", func() { + + g.By("creating test successful build config") + err := oc.Run("create").Args("-f", successfulSTIBuildValueFrom).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("starting test build") + br, err := exutil.StartBuildAndWait(oc, "mys2itest") + o.Expect(err).NotTo(o.HaveOccurred()) + br.AssertSuccess() + + logs, _ := br.Logs() + o.Expect(logs).To(o.ContainSubstring("FIELDREF_ENV=mys2itest-1")) + o.Expect(logs).To(o.ContainSubstring("CONFIGMAPKEYREF_ENV=myvalue")) + o.Expect(logs).To(o.ContainSubstring("SECRETKEYREF_ENV=developer")) + o.Expect(logs).To(o.ContainSubstring("FIELDREF_CLONE_ENV=mys2itest-1")) + o.Expect(logs).To(o.ContainSubstring("FIELDREF_CLONE_CLONE_ENV=mys2itest-1")) + o.Expect(logs).To(o.ContainSubstring("UNAVAILABLE_ENV=$(SOME_OTHER_ENV")) + o.Expect(logs).To(o.ContainSubstring("ESCAPED_ENV=$(MY_ESCAPED_VALUE)")) + + }) + + g.It("should successfully resolve valueFrom in docker build environment variables", func() { + + framework.SkipIfProviderIs("gce") + g.By("creating test successful build config") + err := oc.Run("create").Args("-f", successfulDockerBuildValueFrom).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("starting test build") + br, err := exutil.StartBuildAndWait(oc, "mydockertest") + o.Expect(err).NotTo(o.HaveOccurred()) + br.AssertSuccess() + + logs, _ := br.Logs() + o.Expect(logs).To(o.ContainSubstring("\"FIELDREF_ENV\" \"mydockertest-1\"")) + o.Expect(logs).To(o.ContainSubstring("\"CONFIGMAPKEYREF_ENV\" \"myvalue\"")) + o.Expect(logs).To(o.ContainSubstring("\"SECRETKEYREF_ENV\" \"developer\"")) + o.Expect(logs).To(o.ContainSubstring("\"FIELDREF_CLONE_ENV\" \"mydockertest-1\"")) + o.Expect(logs).To(o.ContainSubstring("\"FIELDREF_CLONE_CLONE_ENV\" \"mydockertest-1\"")) + o.Expect(logs).To(o.ContainSubstring("\"UNAVAILABLE_ENV\" \"$(SOME_OTHER_ENV)\"")) + o.Expect(logs).To(o.ContainSubstring("\"ESCAPED_ENV\" \"$(MY_ESCAPED_VALUE)\"")) + + }) + + g.It("should fail resolving unresolvable valueFrom in sti build environment variable references", func() { + + g.By("creating test build config") + err := oc.Run("create").Args("-f", failedSTIBuildValueFrom).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("starting test build") + br, _ := exutil.StartBuildAndWait(oc, "mys2itest") + br.AssertFailure() + + o.Expect(strings.Contains(string(br.Build.Status.Reason), "UnresolvableEnvironmentVariable")).To(o.BeTrue()) + o.Expect(strings.Contains(br.Build.Status.Message, "unsupported fieldPath: metadata.nofield")).To(o.BeTrue()) + o.Expect(strings.Contains(br.Build.Status.Message, "key nokey not found in config map myconfigmap")).To(o.BeTrue()) + o.Expect(strings.Contains(br.Build.Status.Message, "key nousername not found in secret mysecret")).To(o.BeTrue()) + + }) + + g.It("should fail resolving unresolvable valueFrom in docker build environment variable references", func() { + + g.By("creating test build config") + err := oc.Run("create").Args("-f", failedDockerBuildValueFrom).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("starting test build") + br, _ := exutil.StartBuildAndWait(oc, "mydockertest") + br.AssertFailure() + + o.Expect(strings.Contains(string(br.Build.Status.Reason), "UnresolvableEnvironmentVariable")).To(o.BeTrue()) + o.Expect(strings.Contains(br.Build.Status.Message, "unsupported fieldPath: metadata.nofield")).To(o.BeTrue()) + o.Expect(strings.Contains(br.Build.Status.Message, "key nokey not found in config map myconfigmap")).To(o.BeTrue()) + o.Expect(strings.Contains(br.Build.Status.Message, "key nousername not found in secret mysecret")).To(o.BeTrue()) + + }) +}) diff --git a/test/extended/testdata/bindata.go b/test/extended/testdata/bindata.go index b6a1f91f7511..c810e5f388e4 100644 --- a/test/extended/testdata/bindata.go +++ b/test/extended/testdata/bindata.go @@ -169,6 +169,13 @@ // test/extended/testdata/test-s2i-no-outputname.json // test/extended/testdata/test-secret-build.json // test/extended/testdata/test-secret.json +// test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml +// test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml +// test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml +// test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml +// test/extended/testdata/valuefrom/test-configmap.yaml +// test/extended/testdata/valuefrom/test-is.json +// test/extended/testdata/valuefrom/test-secret.yaml // test/extended/testdata/weighted-router.yaml // test/integration/testdata/project-request-template-with-quota.yaml // test/integration/testdata/test-buildcli-beta2.json @@ -9813,6 +9820,356 @@ func testExtendedTestdataTestSecretJson() (*asset, error) { return a, nil } +var _testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYaml = []byte(` +apiVersion: v1 +kind: BuildConfig +metadata: + name: mydockertest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: '7.0' + strategy: + type: Docker + dockerStrategy: + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.nofield + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: nokey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: nousername + + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 +`) + +func testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYamlBytes() ([]byte, error) { + return _testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYaml, nil +} + +func testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYaml() (*asset, error) { + bytes, err := testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _testExtendedTestdataValuefromFailedStiBuildValueFromConfigYaml = []byte(`apiVersion: v1 +kind: BuildConfig +metadata: + name: mys2itest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: 7.0/test/test-app + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: centos/php-70-centos7 + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.nofield + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: nokey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: nousername + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 +`) + +func testExtendedTestdataValuefromFailedStiBuildValueFromConfigYamlBytes() ([]byte, error) { + return _testExtendedTestdataValuefromFailedStiBuildValueFromConfigYaml, nil +} + +func testExtendedTestdataValuefromFailedStiBuildValueFromConfigYaml() (*asset, error) { + bytes, err := testExtendedTestdataValuefromFailedStiBuildValueFromConfigYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYaml = []byte(` +apiVersion: v1 +kind: BuildConfig +metadata: + name: mydockertest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: '7.0' + strategy: + type: Docker + dockerStrategy: + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: mykey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: username + - name: FIELDREF_CLONE_ENV + value: $(FIELDREF_ENV) + - name: FIELDREF_CLONE_CLONE_ENV + value: $(FIELDREF_CLONE_ENV) + - name: UNAVAILABLE_ENV + value: $(SOME_OTHER_ENV) + - name: ESCAPED_ENV + value: $$(MY_ESCAPED_VALUE) + + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 +`) + +func testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYamlBytes() ([]byte, error) { + return _testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYaml, nil +} + +func testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYaml() (*asset, error) { + bytes, err := testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYaml = []byte(`apiVersion: v1 +kind: BuildConfig +metadata: + name: mys2itest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: 7.0/test/test-app + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: centos/php-70-centos7 + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: mykey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: username + - name: FIELDREF_CLONE_ENV + value: $(FIELDREF_ENV) + - name: FIELDREF_CLONE_CLONE_ENV + value: $(FIELDREF_CLONE_ENV) + - name: UNAVAILABLE_ENV + value: $(SOME_OTHER_ENV) + - name: ESCAPED_ENV + value: $$(MY_ESCAPED_VALUE) + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 +`) + +func testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYamlBytes() ([]byte, error) { + return _testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYaml, nil +} + +func testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYaml() (*asset, error) { + bytes, err := testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _testExtendedTestdataValuefromTestConfigmapYaml = []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfigmap +data: + mykey: myvalue +`) + +func testExtendedTestdataValuefromTestConfigmapYamlBytes() ([]byte, error) { + return _testExtendedTestdataValuefromTestConfigmapYaml, nil +} + +func testExtendedTestdataValuefromTestConfigmapYaml() (*asset, error) { + bytes, err := testExtendedTestdataValuefromTestConfigmapYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/test-configmap.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _testExtendedTestdataValuefromTestIsJson = []byte(`{ + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "test" + } +} +`) + +func testExtendedTestdataValuefromTestIsJsonBytes() ([]byte, error) { + return _testExtendedTestdataValuefromTestIsJson, nil +} + +func testExtendedTestdataValuefromTestIsJson() (*asset, error) { + bytes, err := testExtendedTestdataValuefromTestIsJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/test-is.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _testExtendedTestdataValuefromTestSecretYaml = []byte(`apiVersion: v1 +kind: Secret +metadata: + name: mysecret +data: + password: cGFzc3dvcmQ= + username: ZGV2ZWxvcGVy +type: kubernetes.io/basic-auth +`) + +func testExtendedTestdataValuefromTestSecretYamlBytes() ([]byte, error) { + return _testExtendedTestdataValuefromTestSecretYaml, nil +} + +func testExtendedTestdataValuefromTestSecretYaml() (*asset, error) { + bytes, err := testExtendedTestdataValuefromTestSecretYamlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "test/extended/testdata/valuefrom/test-secret.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _testExtendedTestdataWeightedRouterYaml = []byte(`apiVersion: v1 kind: Template parameters: @@ -19754,6 +20111,13 @@ var _bindata = map[string]func() (*asset, error){ "test/extended/testdata/test-s2i-no-outputname.json": testExtendedTestdataTestS2iNoOutputnameJson, "test/extended/testdata/test-secret-build.json": testExtendedTestdataTestSecretBuildJson, "test/extended/testdata/test-secret.json": testExtendedTestdataTestSecretJson, + "test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml": testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYaml, + "test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml": testExtendedTestdataValuefromFailedStiBuildValueFromConfigYaml, + "test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml": testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYaml, + "test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml": testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYaml, + "test/extended/testdata/valuefrom/test-configmap.yaml": testExtendedTestdataValuefromTestConfigmapYaml, + "test/extended/testdata/valuefrom/test-is.json": testExtendedTestdataValuefromTestIsJson, + "test/extended/testdata/valuefrom/test-secret.yaml": testExtendedTestdataValuefromTestSecretYaml, "test/extended/testdata/weighted-router.yaml": testExtendedTestdataWeightedRouterYaml, "test/integration/testdata/project-request-template-with-quota.yaml": testIntegrationTestdataProjectRequestTemplateWithQuotaYaml, "test/integration/testdata/test-buildcli-beta2.json": testIntegrationTestdataTestBuildcliBeta2Json, @@ -20146,6 +20510,15 @@ var _bintree = &bintree{nil, map[string]*bintree{ "test-s2i-no-outputname.json": &bintree{testExtendedTestdataTestS2iNoOutputnameJson, map[string]*bintree{}}, "test-secret-build.json": &bintree{testExtendedTestdataTestSecretBuildJson, map[string]*bintree{}}, "test-secret.json": &bintree{testExtendedTestdataTestSecretJson, map[string]*bintree{}}, + "valuefrom": &bintree{nil, map[string]*bintree{ + "failed-docker-build-value-from-config.yaml": &bintree{testExtendedTestdataValuefromFailedDockerBuildValueFromConfigYaml, map[string]*bintree{}}, + "failed-sti-build-value-from-config.yaml": &bintree{testExtendedTestdataValuefromFailedStiBuildValueFromConfigYaml, map[string]*bintree{}}, + "successful-docker-build-value-from-config.yaml": &bintree{testExtendedTestdataValuefromSuccessfulDockerBuildValueFromConfigYaml, map[string]*bintree{}}, + "successful-sti-build-value-from-config.yaml": &bintree{testExtendedTestdataValuefromSuccessfulStiBuildValueFromConfigYaml, map[string]*bintree{}}, + "test-configmap.yaml": &bintree{testExtendedTestdataValuefromTestConfigmapYaml, map[string]*bintree{}}, + "test-is.json": &bintree{testExtendedTestdataValuefromTestIsJson, map[string]*bintree{}}, + "test-secret.yaml": &bintree{testExtendedTestdataValuefromTestSecretYaml, map[string]*bintree{}}, + }}, "weighted-router.yaml": &bintree{testExtendedTestdataWeightedRouterYaml, map[string]*bintree{}}, }}, }}, diff --git a/test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml b/test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml new file mode 100644 index 000000000000..cf625028aa9a --- /dev/null +++ b/test/extended/testdata/valuefrom/failed-docker-build-value-from-config.yaml @@ -0,0 +1,48 @@ + +apiVersion: v1 +kind: BuildConfig +metadata: + name: mydockertest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: '7.0' + strategy: + type: Docker + dockerStrategy: + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.nofield + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: nokey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: nousername + + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 diff --git a/test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml b/test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml new file mode 100644 index 000000000000..a52f7cf05f0c --- /dev/null +++ b/test/extended/testdata/valuefrom/failed-sti-build-value-from-config.yaml @@ -0,0 +1,49 @@ +apiVersion: v1 +kind: BuildConfig +metadata: + name: mys2itest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: 7.0/test/test-app + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: centos/php-70-centos7 + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.nofield + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: nokey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: nousername + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 diff --git a/test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml b/test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml new file mode 100644 index 000000000000..d4c6bbde0228 --- /dev/null +++ b/test/extended/testdata/valuefrom/successful-docker-build-value-from-config.yaml @@ -0,0 +1,56 @@ + +apiVersion: v1 +kind: BuildConfig +metadata: + name: mydockertest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: '7.0' + strategy: + type: Docker + dockerStrategy: + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: mykey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: username + - name: FIELDREF_CLONE_ENV + value: $(FIELDREF_ENV) + - name: FIELDREF_CLONE_CLONE_ENV + value: $(FIELDREF_CLONE_ENV) + - name: UNAVAILABLE_ENV + value: $(SOME_OTHER_ENV) + - name: ESCAPED_ENV + value: $$(MY_ESCAPED_VALUE) + + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 diff --git a/test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml b/test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml new file mode 100644 index 000000000000..0e86d30921ed --- /dev/null +++ b/test/extended/testdata/valuefrom/successful-sti-build-value-from-config.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: BuildConfig +metadata: + name: mys2itest + labels: + name: test +spec: + triggers: [] + runPolicy: Serial + source: + type: Git + git: + uri: 'https://github.com/sclorg/s2i-php-container' + contextDir: 7.0/test/test-app + strategy: + type: Source + sourceStrategy: + from: + kind: DockerImage + name: centos/php-70-centos7 + env: + - name: BUILD_LOGLEVEL + value: "5" + - name: FIELDREF_ENV + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: CONFIGMAPKEYREF_ENV + valueFrom: + configMapKeyRef: + name: myconfigmap + key: mykey + - name: SECRETKEYREF_ENV + valueFrom: + secretKeyRef: + name: mysecret + key: username + - name: FIELDREF_CLONE_ENV + value: $(FIELDREF_ENV) + - name: FIELDREF_CLONE_CLONE_ENV + value: $(FIELDREF_CLONE_ENV) + - name: UNAVAILABLE_ENV + value: $(SOME_OTHER_ENV) + - name: ESCAPED_ENV + value: $$(MY_ESCAPED_VALUE) + output: + to: + kind: ImageStreamTag + name: 'test:latest' + imageLabels: + - name: user-specified-label + value: arbitrary-value + resources: {} + postCommit: {} + nodeSelector: null +status: + lastVersion: 0 diff --git a/test/extended/testdata/valuefrom/test-configmap.yaml b/test/extended/testdata/valuefrom/test-configmap.yaml new file mode 100644 index 000000000000..952ec689d170 --- /dev/null +++ b/test/extended/testdata/valuefrom/test-configmap.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: myconfigmap +data: + mykey: myvalue diff --git a/test/extended/testdata/valuefrom/test-is.json b/test/extended/testdata/valuefrom/test-is.json new file mode 100644 index 000000000000..29631abbc9aa --- /dev/null +++ b/test/extended/testdata/valuefrom/test-is.json @@ -0,0 +1,7 @@ +{ + "kind": "ImageStream", + "apiVersion": "v1", + "metadata": { + "name": "test" + } +} diff --git a/test/extended/testdata/valuefrom/test-secret.yaml b/test/extended/testdata/valuefrom/test-secret.yaml new file mode 100644 index 000000000000..c59cc3abcd2d --- /dev/null +++ b/test/extended/testdata/valuefrom/test-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mysecret +data: + password: cGFzc3dvcmQ= + username: ZGV2ZWxvcGVy +type: kubernetes.io/basic-auth diff --git a/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml b/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml index eebd22ec6acc..5e4a1d4d3dc3 100644 --- a/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml +++ b/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml @@ -3615,6 +3615,14 @@ items: verbs: - get - list + - apiGroups: + - "" + attributeRestrictions: null + resources: + - configmaps + verbs: + - get + - list - apiGroups: - "" attributeRestrictions: null