diff --git a/pkg/build/api/types.go b/pkg/build/api/types.go index d805ce818b20..475bf79f7b72 100644 --- a/pkg/build/api/types.go +++ b/pkg/build/api/types.go @@ -37,6 +37,8 @@ type BuildParameters struct { // Output describes the Docker image the Strategy should produce. Output BuildOutput `json:"output,omitempty" yaml:"output,omitempty"` + + UpstreamImage string `json:"upstreamImage,omitempty" yaml:"upstreamImage,omitempty"` } // BuildStatus represents the status of a build at a point in time. diff --git a/pkg/build/api/v1beta1/types.go b/pkg/build/api/v1beta1/types.go index 2d79b65dbb95..87443fd8cf6f 100644 --- a/pkg/build/api/v1beta1/types.go +++ b/pkg/build/api/v1beta1/types.go @@ -37,6 +37,8 @@ type BuildParameters struct { // Output describes the Docker image the Strategy should produce. Output BuildOutput `json:"output,omitempty" yaml:"output,omitempty"` + + UpstreamImage string `json:"upstreamImage,omitempty" yaml:"upstreamImage,omitempty"` } // BuildStatus represents the status of a build at a point in time. diff --git a/pkg/build/builder/docker.go b/pkg/build/builder/docker.go index 6da46b2af903..e60c26123daa 100644 --- a/pkg/build/builder/docker.go +++ b/pkg/build/builder/docker.go @@ -7,6 +7,7 @@ import ( "net/url" "os" "path/filepath" + "regexp" "strings" "time" @@ -54,10 +55,10 @@ func (d *DockerBuilder) Build() error { if err = d.fetchSource(buildDir); err != nil { return err } - if err = d.dockerBuild(buildDir); err != nil { + if err = d.addBuildParameters(buildDir); err != nil { return err } - if err = d.addImageVars(); err != nil { + if err = d.dockerBuild(buildDir); err != nil { return err } if d.build.Parameters.Output.Registry != "" || d.authPresent { @@ -125,40 +126,52 @@ func (d *DockerBuilder) fetchSource(dir string) error { return d.git.Checkout(dir, d.build.Parameters.Source.Git.Ref) } -// dockerBuild performs a docker build on the source that has been retrieved -func (d *DockerBuilder) dockerBuild(dir string) error { - var noCache bool - if d.build.Parameters.Strategy.DockerStrategy != nil { - if d.build.Parameters.Strategy.DockerStrategy.ContextDir != "" { - dir = filepath.Join(dir, d.build.Parameters.Strategy.DockerStrategy.ContextDir) - } - noCache = d.build.Parameters.Strategy.DockerStrategy.NoCache - } - return buildImage(d.dockerClient, dir, noCache, imageTag(d.build), d.tar) -} +// addBuildParameters checks if an UpstreamImage is set to replace the default base image. +// If that's the case then change the Dockerfile to make the build with the given image. +// Also append the environment variables in the Dockerfile. +func (d *DockerBuilder) addBuildParameters(dir string) error { + dockerfilePath := filepath.Join(dir, d.build.Parameters.Strategy.DockerStrategy.ContextDir, "Dockerfile") -// addImageVars creates a new Dockerfile which adds certain environment -// variables to the previously tagged image -func (d *DockerBuilder) addImageVars() error { - var noCache bool - envVars := getBuildEnvVars(d.build) - tempDir, err := ioutil.TempDir("", "overlay") + fileStat, err := os.Lstat(dockerfilePath) + filePerm := fileStat.Mode() + + fileData, err := ioutil.ReadFile(dockerfilePath) if err != nil { return err } - overlay, err := os.Create(filepath.Join(tempDir, "Dockerfile")) - if err != nil { - return err + + var newFileData string + + if d.build.Parameters.UpstreamImage != "" { + re := regexp.MustCompile(`^FROM \w+.+/+\w+`) + newFileData = re.ReplaceAllString(string(fileData), fmt.Sprintf("FROM %s\n", d.build.Parameters.UpstreamImage)) } - overlay.WriteString(fmt.Sprintf("FROM %s\n", imageTag(d.build))) + + envVars := getBuildEnvVars(d.build) for k, v := range envVars { - overlay.WriteString(fmt.Sprintf("ENV %s %s\n", k, v)) + newFileData = newFileData + fmt.Sprintf("ENV %s %s\n", k, v) } - if err = overlay.Close(); err != nil { + + err = ioutil.WriteFile(dockerfilePath, []byte(newFileData), filePerm) + if err != nil { return err } + + var noCache bool if d.build.Parameters.Strategy.DockerStrategy != nil { noCache = d.build.Parameters.Strategy.DockerStrategy.NoCache } - return buildImage(d.dockerClient, tempDir, noCache, imageTag(d.build), d.tar) + return buildImage(d.dockerClient, dir, noCache, imageTag(d.build), d.tar) +} + +// dockerBuild performs a docker build on the source that has been retrieved +func (d *DockerBuilder) dockerBuild(dir string) error { + var noCache bool + if d.build.Parameters.Strategy.DockerStrategy != nil { + if d.build.Parameters.Strategy.DockerStrategy.ContextDir != "" { + dir = filepath.Join(dir, d.build.Parameters.Strategy.DockerStrategy.ContextDir) + } + noCache = d.build.Parameters.Strategy.DockerStrategy.NoCache + } + return buildImage(d.dockerClient, dir, noCache, imageTag(d.build), d.tar) } diff --git a/pkg/build/builder/sti.go b/pkg/build/builder/sti.go index f291647ffb72..157c4c577517 100644 --- a/pkg/build/builder/sti.go +++ b/pkg/build/builder/sti.go @@ -29,13 +29,18 @@ func NewSTIBuilder(client DockerClient, dockerSocket string, authCfg docker.Auth // Build executes the STI build func (s *STIBuilder) Build() error { request := &sti.STIRequest{ - BaseImage: s.build.Parameters.Strategy.STIStrategy.Image, DockerSocket: s.dockerSocket, Source: s.build.Parameters.Source.Git.URI, Tag: imageTag(s.build), Environment: getBuildEnvVars(s.build), Clean: s.build.Parameters.Strategy.STIStrategy.Clean, } + + if s.build.Parameters.UpstreamImage != "" { + request.BaseImage = s.build.Parameters.UpstreamImage + } else { + request.BaseImage = s.build.Parameters.Strategy.STIStrategy.Image + } if s.build.Parameters.Revision != nil && s.build.Parameters.Revision.Git != nil && s.build.Parameters.Revision.Git.Commit != "" { request.Ref = s.build.Parameters.Revision.Git.Commit