func createBuildEnvironment(config *api.Config) []string { env, err := scripts.GetEnvironment(config) if err != nil { glog.V(3).Infof("No user environment provided (%v)", err) } return append(scripts.ConvertEnvironment(env), scripts.ConvertEnvironmentList(config.Environment)...) }
func (builder *STI) createBuildEnvironment() []string { env, err := scripts.GetEnvironment(builder.config) if err != nil { glog.V(3).Infof("No user environment provided (%v)", err) } return append(scripts.ConvertEnvironment(env), builder.generateConfigEnv()...) }
// Execute runs the specified STI script in the builder image. func (b *STI) Execute(command string, config *api.Config) error { glog.V(2).Infof("Using image name %s", config.BuilderImage) env, err := scripts.GetEnvironment(config) if err != nil { glog.V(1).Infof("No .sti/environment provided (%v)", err) } buildEnv := append(scripts.ConvertEnvironment(env), b.generateConfigEnv()...) errOutput := "" outReader, outWriter := io.Pipe() errReader, errWriter := io.Pipe() defer outReader.Close() defer outWriter.Close() defer errReader.Close() defer errWriter.Close() externalScripts := b.externalScripts[command] // if LayeredBuild is called then all the scripts will be placed inside the image if config.LayeredBuild { externalScripts = false } opts := dockerpkg.RunContainerOptions{ Image: config.BuilderImage, Stdout: outWriter, Stderr: errWriter, PullImage: config.ForcePull, ExternalScripts: externalScripts, ScriptsURL: config.ScriptsURL, Destination: config.Destination, Command: command, Env: buildEnv, PostExec: b.postExecutor, } if !config.LayeredBuild { wg := sync.WaitGroup{} wg.Add(1) uploadDir := filepath.Join(config.WorkingDir, "upload") // TODO: be able to pass a stream directly to the Docker build to avoid the double temp hit r, w := io.Pipe() go func() { var err error defer func() { w.CloseWithError(err) if r := recover(); r != nil { glog.Errorf("recovered panic: %#v", r) } wg.Done() }() err = b.tar.CreateTarStream(uploadDir, false, w) }() opts.Stdin = r defer wg.Wait() } go func(reader io.Reader) { scanner := bufio.NewReader(reader) for { text, err := scanner.ReadString('\n') if err != nil { // we're ignoring ErrClosedPipe, as this is information // the docker container ended streaming logs if glog.V(2) && err != io.ErrClosedPipe { glog.Errorf("Error reading docker stdout, %v", err) } break } if glog.V(2) || config.Quiet != true || command == api.Usage { glog.Info(text) } } }(outReader) go dockerpkg.StreamContainerIO(errReader, &errOutput, glog.Error) err = b.docker.RunContainer(opts) if e, ok := err.(errors.ContainerError); ok { return errors.NewContainerError(config.BuilderImage, e.ErrorCode, errOutput) } return err }
// PostExecute allows to execute post-build actions after the Docker build // finishes. func (b *STI) PostExecute(containerID, location string) error { var ( err error previousImageID string ) if b.incremental && b.config.RemovePreviousImage { if previousImageID, err = b.docker.GetImageID(b.config.Tag); err != nil { glog.Errorf("Error retrieving previous image's metadata: %v", err) } } env, err := scripts.GetEnvironment(b.config) if err != nil { glog.V(1).Infof("No .sti/environment provided (%v)", err) } buildEnv := append(scripts.ConvertEnvironment(env), b.generateConfigEnv()...) runCmd := b.scriptsURL[api.Run] if strings.HasPrefix(runCmd, "image://") { // scripts from inside of the image, we need to strip the image part runCmd = filepath.Join(strings.TrimPrefix(runCmd, "image://"), api.Run) } else { // external scripts, in which case we're taking the directory to which they // were extracted and append scripts dir and name runCmd = filepath.Join(location, "scripts", api.Run) } existingLabels, err := b.docker.GetLabels(b.config.BuilderImage) if err != nil { glog.Errorf("Unable to read existing labels from current builder image %s", b.config.BuilderImage) } opts := dockerpkg.CommitContainerOptions{ Command: append([]string{}, runCmd), Env: buildEnv, ContainerID: containerID, Repository: b.config.Tag, Labels: mergeLabels(util.GenerateOutputImageLabels(b.sourceInfo, b.config), existingLabels), } imageID, err := b.docker.CommitContainer(opts) if err != nil { return errors.NewBuildError(b.config.Tag, err) } b.result.Success = true b.result.ImageID = imageID if len(b.config.Tag) > 0 { glog.V(1).Infof("Successfully built %s", b.config.Tag) } else { glog.V(1).Infof("Successfully built %s", imageID) } if b.incremental && b.config.RemovePreviousImage && previousImageID != "" { glog.V(1).Infof("Removing previously-tagged image %s", previousImageID) if err = b.docker.RemoveImage(previousImageID); err != nil { glog.Errorf("Unable to remove previous image: %v", err) } } if b.config.CallbackURL != "" { b.result.Messages = b.callbackInvoker.ExecuteCallback(b.config.CallbackURL, b.result.Success, b.result.Messages) } return nil }
// Execute runs the specified STI script in the builder image. func (b *STI) Execute(command string, config *api.Config) error { glog.V(2).Infof("Using image name %s", config.BuilderImage) env, err := scripts.GetEnvironment(config) if err != nil { glog.V(1).Infof("No .sti/environment provided (%v)", err) } buildEnv := append(scripts.ConvertEnvironment(env), b.generateConfigEnv()...) uploadDir := filepath.Join(config.WorkingDir, "upload") tarFileName, err := b.tar.CreateTarFile(config.WorkingDir, uploadDir) if err != nil { return err } tarFile, err := b.fs.Open(tarFileName) if err != nil { return err } defer tarFile.Close() errOutput := "" outReader, outWriter := io.Pipe() errReader, errWriter := io.Pipe() defer outReader.Close() defer outWriter.Close() defer errReader.Close() defer errWriter.Close() externalScripts := b.externalScripts[command] // if LayeredBuild is called then all the scripts will be placed inside the image if config.LayeredBuild { externalScripts = false } opts := docker.RunContainerOptions{ Image: config.BuilderImage, Stdout: outWriter, Stderr: errWriter, PullImage: config.ForcePull, ExternalScripts: externalScripts, ScriptsURL: config.ScriptsURL, Destination: config.Destination, Command: command, Env: buildEnv, PostExec: b.postExecutor, } if !config.LayeredBuild { opts.Stdin = tarFile } go func(reader io.Reader) { scanner := bufio.NewReader(reader) for { text, err := scanner.ReadString('\n') if err != nil { // we're ignoring ErrClosedPipe, as this is information // the docker container ended streaming logs if glog.V(2) && err != io.ErrClosedPipe { glog.Errorf("Error reading docker stdout, %v", err) } break } if glog.V(2) || config.Quiet != true || command == api.Usage { glog.Info(text) } } }(outReader) go streamContainerError(errReader, &errOutput, config) err = b.docker.RunContainer(opts) if e, ok := err.(errors.ContainerError); ok { return errors.NewContainerError(config.BuilderImage, e.ErrorCode, errOutput) } return err }
// Execute runs the specified STI script in the builder image. func (b *STI) Execute(command string, user string, config *api.Config) error { glog.V(2).Infof("Using image name %s", config.BuilderImage) env, err := scripts.GetEnvironment(config) if err != nil { glog.V(1).Infof("No user environment provided (%v)", err) } buildEnv := append(scripts.ConvertEnvironment(env), b.generateConfigEnv()...) errOutput := "" outReader, outWriter := io.Pipe() errReader, errWriter := io.Pipe() defer outReader.Close() defer outWriter.Close() defer errReader.Close() defer errWriter.Close() externalScripts := b.externalScripts[command] // if LayeredBuild is called then all the scripts will be placed inside the image if config.LayeredBuild { externalScripts = false } opts := dockerpkg.RunContainerOptions{ Image: config.BuilderImage, Stdout: outWriter, Stderr: errWriter, // The PullImage is false because the PullImage function should be called // before we run the container PullImage: false, ExternalScripts: externalScripts, ScriptsURL: config.ScriptsURL, Destination: config.Destination, Command: command, Env: buildEnv, User: user, PostExec: b.postExecutor, NetworkMode: string(config.DockerNetworkMode), CGroupLimits: config.CGroupLimits, CapDrop: config.DropCapabilities, } // If there are injections specified, override the original assemble script // and wait till all injections are uploaded into the container that runs the // assemble script. injectionComplete := make(chan struct{}) var injectionError error if len(config.Injections) > 0 && command == api.Assemble { workdir, err := b.docker.GetImageWorkdir(config.BuilderImage) if err != nil { return err } util.FixInjectionsWithRelativePath(workdir, &config.Injections) injectedFiles, err := util.ExpandInjectedFiles(config.Injections) if err != nil { return err } rmScript, err := util.CreateInjectedFilesRemovalScript(injectedFiles, "/tmp/rm-injections") if err != nil { return err } defer os.Remove(rmScript) opts.CommandOverrides = func(cmd string) string { return fmt.Sprintf("while [ ! -f %q ]; do sleep 0.5; done; %s; result=$?; source %[1]s; exit $result", "/tmp/rm-injections", cmd) } originalOnStart := opts.OnStart opts.OnStart = func(containerID string) error { defer close(injectionComplete) if err != nil { injectionError = err return err } glog.V(2).Info("starting the injections uploading ...") for _, s := range config.Injections { if err := b.docker.UploadToContainer(s.SourcePath, s.DestinationDir, containerID); err != nil { injectionError = util.HandleInjectionError(s, err) return err } } if err := b.docker.UploadToContainer(rmScript, "/tmp/rm-injections", containerID); err != nil { injectionError = util.HandleInjectionError(api.InjectPath{SourcePath: rmScript, DestinationDir: "/tmp/rm-injections"}, err) return err } if originalOnStart != nil { return originalOnStart(containerID) } return nil } } else { close(injectionComplete) } wg := sync.WaitGroup{} if !config.LayeredBuild { wg.Add(1) uploadDir := filepath.Join(config.WorkingDir, "upload") // TODO: be able to pass a stream directly to the Docker build to avoid the double temp hit r, w := io.Pipe() go func() { // Wait for the injections to complete and check the error. Do not start // streaming the sources when the injection failed. <-injectionComplete if injectionError != nil { wg.Done() return } glog.V(2).Info("starting the source uploading ...") var err error defer func() { w.CloseWithError(err) if r := recover(); r != nil { glog.Errorf("recovered panic: %#v", r) } wg.Done() }() err = b.tar.CreateTarStream(uploadDir, false, w) }() opts.Stdin = r defer wg.Wait() } go func(reader io.Reader) { scanner := bufio.NewReader(reader) for { text, err := scanner.ReadString('\n') if err != nil { // we're ignoring ErrClosedPipe, as this is information // the docker container ended streaming logs if glog.V(2) && err != io.ErrClosedPipe { glog.Errorf("Error reading docker stdout, %v", err) } break } // Nothing is printed when the quiet option is set if config.Quiet { continue } // The log level > 3 forces to use glog instead of printing to stdout if glog.V(3) { glog.Info(text) continue } fmt.Fprintf(os.Stdout, "%s\n", strings.TrimSpace(text)) } }(outReader) go dockerpkg.StreamContainerIO(errReader, &errOutput, glog.Error) err = b.docker.RunContainer(opts) if util.IsTimeoutError(err) { // Cancel waiting for source input if the container timeouts wg.Done() } if e, ok := err.(errors.ContainerError); ok { return errors.NewContainerError(config.BuilderImage, e.ErrorCode, errOutput) } return err }