// this funtion simply abstracts out the first phase of attaching to the container that was originally in line with the RunContainer() method func runContainerAttach(attached chan struct{}, container *docker.Container, opts RunContainerOptions, d *stiDocker) *sync.WaitGroup { attachOpts := docker.AttachToContainerOptions{ Container: container.ID, Success: attached, Stream: true, } if opts.Stdin != nil { attachOpts.InputStream = opts.Stdin attachOpts.Stdin = true } if opts.Stdout != nil { attachOpts.OutputStream = opts.Stdout attachOpts.Stdout = true } if opts.Stderr != nil { attachOpts.ErrorStream = opts.Stderr attachOpts.Stderr = true } wg := sync.WaitGroup{} go func() { wg.Add(1) defer wg.Done() if err := d.client.AttachToContainer(attachOpts); err != nil { glog.Errorf("Unable to attach container with %v", attachOpts) } }() return &wg }
// this funtion simply abstracts out the second phase of attaching to the container that was originally in line with the RunContainer() method func runContainerAttachTwo(attached2 chan struct{}, container *docker.Container, opts RunContainerOptions, d *stiDocker, wg sync.WaitGroup) { attachOpts2 := docker.AttachToContainerOptions{ Container: container.ID, Success: attached2, Stream: true, OutputStream: opts.Stdout, Stdout: true, } if opts.Stderr != nil { attachOpts2.Stderr = true attachOpts2.ErrorStream = opts.Stderr } go func() { wg.Add(1) defer wg.Done() if err := d.client.AttachToContainer(attachOpts2); err != nil { glog.Errorf("Unable to attach container with %v", attachOpts2) } }() }
// RunContainer creates and starts a container using the image specified in the options with the ability // to stream input or output func (d *stiDocker) RunContainer(opts RunContainerOptions) (err error) { // get info about the specified image image := getImageName(opts.Image) var imageMetadata *docker.Image if opts.PullImage { imageMetadata, err = d.CheckAndPullImage(image) } else { imageMetadata, err = d.client.InspectImage(image) } if err != nil { glog.Errorf("Unable to get image metadata for %s: %v", image, err) return err } // 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 commandBaseDir = filepath.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) } cmd := []string{filepath.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, filepath.Join(commandBaseDir, string(opts.Command)))} } config := docker.Config{ Image: image, Cmd: cmd, } if opts.Env != nil { config.Env = opts.Env } if opts.Stdin != nil { config.OpenStdin = true config.StdinOnce = true } if opts.Stdout != nil { config.AttachStdout = true } glog.V(2).Infof("Creating container using config: %+v", config) container, err := d.client.CreateContainer(docker.CreateContainerOptions{Name: "", Config: &config}) if err != nil { return err } defer d.RemoveContainer(container.ID) glog.V(2).Infof("Attaching to container") attached := make(chan struct{}) attachOpts := docker.AttachToContainerOptions{ Container: container.ID, Success: attached, Stream: true, } if opts.Stdin != nil { attachOpts.InputStream = opts.Stdin attachOpts.Stdin = true } else if opts.Stdout != nil { attachOpts.OutputStream = opts.Stdout attachOpts.Stdout = true } if opts.Stderr != nil { attachOpts.ErrorStream = opts.Stderr attachOpts.Stderr = true } wg := sync.WaitGroup{} go func() { wg.Add(1) defer wg.Done() if err := d.client.AttachToContainer(attachOpts); err != nil { glog.Errorf("Unable to attach container with %v", attachOpts) } }() attached <- <-attached // If attaching both stdin and stdout or stderr, attach stdout and stderr in // a second goroutine // TODO remove this goroutine when docker 1.4 will be in broad usage, // see: https://github.com/docker/docker/commit/f936a10d8048f471d115978472006e1b58a7c67d if opts.Stdin != nil && opts.Stdout != nil { attached2 := make(chan struct{}) attachOpts2 := docker.AttachToContainerOptions{ Container: container.ID, Success: attached2, Stream: true, OutputStream: opts.Stdout, Stdout: true, } if opts.Stderr != nil { attachOpts2.Stderr = true attachOpts2.ErrorStream = opts.Stderr } go func() { wg.Add(1) defer wg.Done() if err := d.client.AttachToContainer(attachOpts2); err != nil { glog.Errorf("Unable to attach container with %v", attachOpts2) } }() attached2 <- <-attached2 } glog.V(2).Infof("Starting container") if err = d.client.StartContainer(container.ID, nil); err != nil { return err } if opts.OnStart != nil { if err = opts.OnStart(); err != nil { return err } } glog.V(2).Infof("Waiting for container") exitCode, err := d.client.WaitContainer(container.ID) wg.Wait() if err != nil { return err } glog.V(2).Infof("Container exited") if exitCode != 0 { return errors.NewContainerError(container.Name, exitCode, "") } if opts.PostExec != nil { glog.V(2).Infof("Invoking postExecution function") if err = opts.PostExec.PostExecute(container.ID, tarDestination); err != nil { return err } } return nil }