From d2e8a4602cd97b53a9dfa387f66e60dd2a988aa0 Mon Sep 17 00:00:00 2001 From: Michal Fojtik Date: Tue, 24 Feb 2015 16:00:43 +0100 Subject: [PATCH] Add support for .sti/environment file --- README.md | 13 +++++ pkg/api/scripts.go | 5 ++ pkg/build/strategies/onbuild/onbuild.go | 7 +++ pkg/build/strategies/sti/sti.go | 18 +++++- pkg/scripts/environment.go | 73 +++++++++++++++++++++++++ pkg/scripts/environment_test.go | 16 ++++++ 6 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 pkg/scripts/environment.go create mode 100644 pkg/scripts/environment_test.go diff --git a/README.md b/README.md index 490e26877..2acc854b4 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,18 @@ image: Additionally for the best user experience and optimized sti operation we suggest image to have `/bin/sh` and tar command inside. +Users can also set extra environment variables in the application source code, +which are passed to the build and the `assemble` script consumes them. All +environment variables are also present in the output application image. These +variables are defined in `.sti/environment` file inside the application sources. +The format of this file is a simple key-value, for example: + +``` +FOO=bar +``` + +In this case, the value of `FOO` environment variable will be set to `bar`. + For detailed description of the requirements and the scripts along with examples see [builder_image.md](https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md) @@ -51,6 +63,7 @@ For detailed description of the requirements and the scripts along with examples 1. `sti` creates a container based on the build image and passes it a tar file that contains: 1. The application source in `src` 1. The build artifacts in `artifacts` (if applicable - see [incremental builds](#incremental-builds)) +1. `sti` sets the environment variables from `.sti/environment` (optional) 1. `sti` starts the container and runs its `assemble` script 1. `sti` waits for the container to finish 1. `sti` commits the container, setting the CMD for the output image to be the `run` script and tagging the image with the name provided. diff --git a/pkg/api/scripts.go b/pkg/api/scripts.go index e573c54de..f53329cc9 100644 --- a/pkg/api/scripts.go +++ b/pkg/api/scripts.go @@ -9,6 +9,11 @@ const ( SaveArtifacts = "save-artifacts" // Usage is the name of the script responsible for printing the builder image's short info. Usage = "usage" + + // Environment contains list of key value pairs that will be set during the + // STI build. Users can use this file to provide extra configuration + // depending on the builder image used. + Environment = "environment" ) const ( diff --git a/pkg/build/strategies/onbuild/onbuild.go b/pkg/build/strategies/onbuild/onbuild.go index 7cff63c34..ea081ece9 100644 --- a/pkg/build/strategies/onbuild/onbuild.go +++ b/pkg/build/strategies/onbuild/onbuild.go @@ -13,6 +13,7 @@ import ( "github.com/openshift/source-to-image/pkg/build/strategies/sti" "github.com/openshift/source-to-image/pkg/docker" "github.com/openshift/source-to-image/pkg/git" + "github.com/openshift/source-to-image/pkg/scripts" "github.com/openshift/source-to-image/pkg/tar" "github.com/openshift/source-to-image/pkg/util" ) @@ -124,6 +125,12 @@ func (b *OnBuild) CreateDockerfile(request *api.Request) error { if err != nil { return err } + env, err := scripts.GetEnvironment(request) + if err != nil { + glog.V(1).Infof("Environment: %v", err) + } else { + buffer.WriteString(scripts.ConvertEnvironmentToDocker(env)) + } // If there is an assemble script present, run it as part of the build process // as the last thing. if b.hasAssembleScript(request) { diff --git a/pkg/build/strategies/sti/sti.go b/pkg/build/strategies/sti/sti.go index e74e689e3..9bce147d4 100644 --- a/pkg/build/strategies/sti/sti.go +++ b/pkg/build/strategies/sti/sti.go @@ -209,10 +209,17 @@ func (b *STI) PostExecute(containerID string, location string) error { } } + env, err := scripts.GetEnvironment(b.request) + if err != nil { + glog.V(1).Infof("No .sti/environment provided (%v)", err) + } + + buildEnv := append(scripts.ConvertEnvironment(env), b.generateConfigEnv()...) + cmd := []string{} opts := docker.CommitContainerOptions{ Command: append(cmd, filepath.Join(location, api.Run)), - Env: b.generateConfigEnv(), + Env: buildEnv, ContainerID: containerID, Repository: b.request.Tag, } @@ -296,6 +303,13 @@ func (b *STI) Save(request *api.Request) (err error) { func (b *STI) Execute(command string, request *api.Request) error { glog.V(2).Infof("Using image name %s", request.BaseImage) + env, err := scripts.GetEnvironment(request) + if err != nil { + glog.V(1).Infof("No .sti/environment provided (%v)", err) + } + + buildEnv := append(scripts.ConvertEnvironment(env), b.generateConfigEnv()...) + uploadDir := filepath.Join(request.WorkingDir, "upload") tarFileName, err := b.tar.CreateTarFile(request.WorkingDir, uploadDir) if err != nil { @@ -329,7 +343,7 @@ func (b *STI) Execute(command string, request *api.Request) error { ScriptsURL: request.ScriptsURL, Location: request.Location, Command: command, - Env: b.generateConfigEnv(), + Env: buildEnv, PostExec: b.postExecutor, } if !request.LayeredBuild { diff --git a/pkg/scripts/environment.go b/pkg/scripts/environment.go new file mode 100644 index 000000000..8fe9725f1 --- /dev/null +++ b/pkg/scripts/environment.go @@ -0,0 +1,73 @@ +package scripts + +import ( + "bufio" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/golang/glog" + "github.com/openshift/source-to-image/pkg/api" +) + +// Environment represents a single environment variable definition +type Environment struct { + Name string + Value string +} + +// GetEnvironment gets the .sti/environment file located in the sources and +// parse it into []environment +func GetEnvironment(request *api.Request) ([]Environment, error) { + envPath := filepath.Join(request.WorkingDir, api.Source, ".sti", api.Environment) + if _, err := os.Stat(envPath); os.IsNotExist(err) { + return nil, errors.New("no evironment file found in application sources") + } + + f, err := os.Open(envPath) + if err != nil { + return nil, errors.New("unable to read .sti/environment file") + } + defer f.Close() + + result := []Environment{} + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + s := scanner.Text() + // Allow for comments in environment file + if strings.HasPrefix(s, "#") { + continue + } + parts := strings.SplitN(s, "=", 2) + if len(parts) != 2 { + continue + } + e := Environment{ + Name: strings.TrimSpace(parts[0]), + Value: strings.TrimSpace(parts[1]), + } + glog.V(1).Infof("Setting '%s' to '%s'", e.Name, e.Value) + result = append(result, e) + } + + return result, scanner.Err() +} + +// ConvertEnvironment converts the []Environment to "key=val" strings +func ConvertEnvironment(env []Environment) (result []string) { + for _, e := range env { + result = append(result, fmt.Sprintf("%s=%s", e.Name, e.Value)) + } + return +} + +// ConvertEnvironmentToDocker converts the []Environment into Dockerfile format +func ConvertEnvironmentToDocker(env []Environment) (result string) { + for _, e := range env { + result += fmt.Sprintf("ENV %s %s\n", e.Name, e.Value) + } + return +} diff --git a/pkg/scripts/environment_test.go b/pkg/scripts/environment_test.go new file mode 100644 index 000000000..cf35741fc --- /dev/null +++ b/pkg/scripts/environment_test.go @@ -0,0 +1,16 @@ +package scripts + +import "testing" + +func TestConvertEnvironment(t *testing.T) { + env := []Environment{ + {"FOO", "BAR"}, + } + result := ConvertEnvironment(env) + if len(result) != 1 { + t.Errorf("Expected 1 item, got %d", len(result)) + } + if result[0] != "FOO=BAR" { + t.Errorf("Expected FOO=BAR, got %v", result) + } +}