// 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 } } }
// DockerPush is the cli action for 'godo docker-push' func DockerPush(c *cli.Context) { dockerClient := dockutil.ClientOrDie() cfg := config.ReadOrDie(c.String(FlagConfigFile)) if cfg.Docker.ImageName == "" { log.Err("Docker image name was empty") os.Exit(1) } pio := docker.PushImageOptions{ Name: cfg.Docker.ImageName, Tag: cfg.Docker.GetTag(), OutputStream: os.Stdout, } authFileLoc := cfg.Docker.Push.GetAuthFileLocation() authFile, err := os.Open(authFileLoc) if err != nil { log.Err("Reading Docker auth file %s [%s]", authFileLoc, err) os.Exit(1) } defer func() { if err := authFile.Close(); err != nil { log.Err("Closing Docker auth file %s [%s]", authFileLoc, err) } }() auths, err := docker.NewAuthConfigurations(authFile) if err != nil { log.Err("Parsing auth file %s [%s]", authFileLoc, err) os.Exit(1) } registry := "https://index.docker.io/v1/" spl := strings.Split(pio.Name, "/") if len(spl) == 3 { registry = spl[0] } auth, ok := auths.Configs[registry] if !ok { log.Err("Registry %s in your image %s is not in auth file %s ", registry, pio.Name, authFileLoc) os.Exit(1) } if err := dockerClient.PushImage(pio, auth); err != nil { log.Err("Pushing Docker image %s [%s]", pio.Name, err) os.Exit(1) } log.Info("Successfully pushed Docker image %s:%s", pio.Name, pio.Tag) }
// execOrDie prints the command before executing it, executes it and returns its output. // if the command failed, calls log.Die with a helpful error message func runOrDie(cmd *exec.Cmd, env []string) []byte { cmd.Env = env log.Info(cmdStr(cmd)) log.Debug("Env: %s", envStr(cmd)) out, err := cmd.CombinedOutput() if err != nil { var s string if len(out) > 0 { s += fmt.Sprintf("%s\n", string(out)) } if err != nil { s += err.Error() } log.Die(s) } return out }
// DockerBuild is the CLI action for 'godo docker-build' func DockerBuild(c *cli.Context) { dockerClient := dockutil.ClientOrDie() cfg := config.ReadOrDie(c.String(FlagConfigFile)) if cfg.Docker.ImageName == "" { log.Err("Docker image name was empty") os.Exit(1) } dockerfileLocation := cfg.Docker.Build.GetDockerfileLocation() dockerfileBytes, err := ioutil.ReadFile(dockerfileLocation) if err != nil { log.Err("Reading Dockerfile %s [%s]", dockerfileLocation, err) os.Exit(1) } t := time.Now() buf := bytes.NewBuffer(nil) tr := tar.NewWriter(buf) tr.WriteHeader(&tar.Header{ Name: "Dockerfile", Size: int64(len(dockerfileBytes)), ModTime: t, AccessTime: t, ChangeTime: t, }) tr.Write(dockerfileBytes) buildCtx, err := filepath.Abs(cfg.Docker.Build.Context.GetDirectory()) if err != nil { log.Err("Invalid Docker build context %s [%s]", cfg.Docker.Build.Context.GetDirectory(), err) os.Exit(1) } skipSet := cfg.Docker.Build.Context.GetSkips() err = tarDir(buildCtx, tr, func(path string, fi os.FileInfo) bool { if _, ok := skipSet[path]; ok { return true } if strings.Contains(path, ".git") { return true } if fi.Name() == "Dockerfile" { return true } return false }) if err != nil { log.Err("Archiving the build context directory %s [%s]", buildCtx, err) os.Exit(1) } if err := tr.Close(); err != nil { log.Err("Closing the build context archive preparing to send it to the Docker daemon [%s]", err) os.Exit(1) } opts := docker.BuildImageOptions{ Name: fmt.Sprintf("%s:%s", cfg.Docker.ImageName, cfg.Docker.GetTag()), InputStream: buf, Dockerfile: "Dockerfile", OutputStream: os.Stdout, RmTmpContainer: true, Pull: true, } if err := dockerClient.BuildImage(opts); err != nil { log.Err("Building image %s [%s]", cfg.Docker.ImageName, err) os.Exit(1) } log.Info("Successfully built Docker image %s", opts.Name) }
// 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 } } }