// Run executes a single Run command against the current container using exec(). // Since exec does not allow ENV or WORKINGDIR to be set, we force the execution of // the user command into a shell and perform those operations before. Since RUN // requires /bin/sh, we can use both 'cd' and 'export'. func (e *ClientExecutor) Run(run Run, config docker.Config) error { args := make([]string, len(run.Args)) copy(args, run.Args) if runtime.GOOS == "windows" { if len(config.WorkingDir) > 0 { args[0] = fmt.Sprintf("cd %s && %s", bashQuote(config.WorkingDir), args[0]) } // TODO: implement windows ENV args = append([]string{"cmd", "/S", "/C"}, args...) } else { if len(config.WorkingDir) > 0 { args[0] = fmt.Sprintf("cd %s && %s", bashQuote(config.WorkingDir), args[0]) } if len(config.Env) > 0 { args[0] = exportEnv(config.Env) + args[0] } args = append([]string{"/bin/sh", "-c"}, args...) } config.Cmd = args exec, err := e.Client.CreateExec(docker.CreateExecOptions{ Cmd: config.Cmd, Container: e.Container.ID, AttachStdout: true, AttachStderr: true, User: config.User, }) if err != nil { return err } if err := e.Client.StartExec(exec.ID, docker.StartExecOptions{ OutputStream: e.Out, ErrorStream: e.ErrOut, }); err != nil { return err } status, err := e.Client.InspectExec(exec.ID) if err != nil { return err } if status.ExitCode != 0 { return fmt.Errorf("running '%s' failed with exit code %d", strings.Join(args, " "), status.ExitCode) } return nil }
// this funtion simply abstracts out the tar related processing that was originally inline in RunContainer() func runContainerTar(opts RunContainerOptions, config docker.Config, imageMetadata *docker.Image) (docker.Config, string) { tarDestination := "" if opts.TargetImage { return config, tarDestination } // base directory for all STI commands var commandBaseDir string // untar operation destination directory tarDestination = opts.Destination if len(tarDestination) == 0 { tarDestination = getDestination(imageMetadata) } if opts.ExternalScripts { // for external scripts we must always append 'scripts' because this is // the default subdirectory inside tar for them // NOTE: We use path.Join instead of filepath.Join to avoid converting the // path to UNC (Windows) format as we always run this inside container. commandBaseDir = path.Join(tarDestination, "scripts") glog.V(2).Infof("Both scripts and untarred source will be placed in '%s'", tarDestination) } else { // for internal scripts we can have separate path for scripts and untar operation destination scriptsURL := opts.ScriptsURL if len(scriptsURL) == 0 { scriptsURL = getScriptsURL(imageMetadata) } commandBaseDir = strings.TrimPrefix(scriptsURL, "image://") glog.V(2).Infof("Base directory for STI scripts is '%s'. Untarring destination is '%s'.", commandBaseDir, tarDestination) } // NOTE: We use path.Join instead of filepath.Join to avoid converting the // path to UNC (Windows) format as we always run this inside container. cmd := []string{path.Join(commandBaseDir, string(opts.Command))} // when calling assemble script with Stdin parameter set (the tar file) // we need to first untar the whole archive and only then call the assemble script if opts.Stdin != nil && (opts.Command == api.Assemble || opts.Command == api.Usage) { cmd = []string{"/bin/sh", "-c", fmt.Sprintf("tar -C %s -xf - && %s", tarDestination, cmd[0])} if opts.CommandOverrides != nil { cmd = []string{"/bin/sh", "-c", opts.CommandOverrides(strings.Join(cmd[2:], " "))} } } glog.V(5).Infof("Running %q command in container ...", strings.Join(cmd, " ")) config.Cmd = cmd return config, tarDestination }