// Build is the CLI handler for 'godo build' func Build(c *cli.Context) { cfg := config.ReadOrDie(c.String(FlagConfigFile)) paths := PathsOrDie() dockerClient := docker.ClientOrDie() imgStr := fmt.Sprintf("%s:%s", cfg.Build.ImageName, cfg.Build.ImageTag) img, err := docker.ParseImageFromName(imgStr) if err != nil { log.Err("error parsing docker image %s (%s)", imgStr, err) os.Exit(1) } rmContainerCh := make(chan func()) stdOutCh := make(chan docker.Log) stdErrCh := make(chan docker.Log) exitCodeCh := make(chan int) errCh := make(chan error) projName := filepath.Base(paths.CWD) binaryName := cfg.Build.GetOutputBinary(projName) go docker.Run( dockerClient, img, "build", paths.CWD, docker.ContainerGopath(cfg.Build.Gopath, paths.PackageName), fmt.Sprintf("go build -o %s .", binaryName), cfg.Build.Env, rmContainerCh, stdOutCh, stdErrCh, exitCodeCh, errCh, ) for { select { case rmContainerFn := <-rmContainerCh: defer rmContainerFn() case l := <-stdOutCh: log.Info("%s", l) case l := <-stdErrCh: log.Warn("%s", l) case err := <-errCh: log.Err("%s", err) return case i := <-exitCodeCh: log.Info("exited with code %d", i) return } } }
// Run runs cmd in the given image using the docker client cl. It mounts cwd into containerMount in the running container and sends on the following channels: // // - rmContainerCh: a function closure that the receiver should call, after they receive on errCh or exitCodeCh, to remove the container. this is commonly done with a 'defer' // - stdOut: all logs from STDOUT in the container. this may never receive // - stdErr: all logs from STDERR in the container. this may never receive // - exitCodeCh: the exit code of the container // - errCh: any error in setting up or running the container. if errCh receives, exitCodeCh may not receive func Run( cl *docker.Client, image *Image, taskName, cwd, containerMount, cmd string, env []string, rmContainerCh chan<- func(), stdOut chan<- Log, stdErr chan<- Log, exitCodeCh chan<- int, errCh chan<- error, ) { mounts := []docker.Mount{ {Name: "pwd", Source: cwd, Destination: containerMount, Mode: "rxw"}, } cmdSpl := strings.Split(cmd, " ") containerName := NewContainerName(taskName, cwd) createContainerOpts, hostConfig := CreateAndStartContainerOpts(image.String(), containerName, cmdSpl, env, mounts, containerMount) if err := EnsureImage(cl, image.String(), func() (io.Writer, error) { return os.Stdout, nil }); err != nil { errCh <- err return } container, err := cl.CreateContainer(createContainerOpts) if err != nil { errCh <- err } rmContainerCh <- func() { if err := cl.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID, Force: true}); err != nil { log.Warn("Error removing container %s (%s)", container.ID, err) } } log.Debug(CmdStr(createContainerOpts, hostConfig)) attachOpts := AttachToContainerOpts(container.ID, NewChanWriter(stdOut), NewChanWriter(stdErr)) // attach before the container starts, so we get all the logs etc... go AttachAndWait(cl, container.ID, attachOpts, exitCodeCh, errCh) if startErr := cl.StartContainer(container.ID, &hostConfig); startErr != nil { errCh <- err return } }
// Custom is the CLI action for 'godo custom ...' commands func Custom(c *cli.Context) { cfg := config.ReadOrDie(c.String(FlagConfigFile)) if c.Bool(ListCustomFlag) { for _, customTarget := range cfg.Custom { log.Info("'%s' - %s", customTarget.Name, customTarget.Description) } return } if len(c.Args()) < 1 || c.Args()[0] == "" { log.Err("you must call this command as 'godo custom <target>'") os.Exit(1) } customName := c.Args()[0] customMap := make(map[string]config.CustomTarget) for _, customTarget := range cfg.Custom { customMap[customTarget.Name] = customTarget } target, ok := customMap[customName] if !ok { log.Err("no custom target '%s' found", customName) os.Exit(1) } dockerImage, err := docker.ParseImageFromName(fmt.Sprintf("%s:%s", target.ImageName, target.ImageTag)) if err != nil { log.Err("invalid image name %s:%s (%s)", target.ImageName, target.ImageTag) os.Exit(1) } dockerCl := docker.ClientOrDie() paths := PathsOrDie() log.Info("executing %s in a %s:%s container", customName, target.ImageName, target.ImageTag) rmContainerCh := make(chan func()) stdOutCh := make(chan docker.Log) stdErrCh := make(chan docker.Log) exitCodeCh := make(chan int) errCh := make(chan error) go docker.Run( dockerCl, dockerImage, customName, paths.CWD, target.MountTarget, target.Command, target.Envs, rmContainerCh, stdOutCh, stdErrCh, exitCodeCh, errCh, ) for { select { case fn := <-rmContainerCh: defer fn() case l := <-stdOutCh: log.Info("%s", l) case l := <-stdErrCh: log.Warn("%s", l) case i := <-exitCodeCh: log.Info("exited with code %d", i) return case err := <-errCh: log.Err("%s", err) return } } }