// NewDeployOptions constructor func NewDeployOptions(c util.Settings, e *util.Environment) (*PipelineOptions, error) { pipelineOpts, err := NewPipelineOptions(c, e) if err != nil { return nil, err } // default to last build output if none defined target, _ := c.String("target") if target == "" { found, err := util.Exists("./.wercker/latest/output") if err == nil && found { util.RootLogger().Println("No target specified, using recent build output.") pipelineOpts.ProjectPath, _ = filepath.Abs("./.wercker/latest/output") } } // if the deploy target path does not have a wercker.yml, use the current one werckerYml, _ := c.String("wercker-yml") if werckerYml == "" { found, _ := util.Exists(filepath.Join(pipelineOpts.ProjectPath, "wercker.yml")) if !found { pipelineOpts.WerckerYml = "./wercker.yml" } } if pipelineOpts.RunID == "" { pipelineOpts.RunID = uuid.NewRandom().String() } return pipelineOpts, nil }
// Execute actually sends the commands for the step. func (s *ExternalStep) Execute(sessionCtx context.Context, sess *Session) (int, error) { err := s.SetupGuest(sessionCtx, sess) if err != nil { return 1, err } _, _, err = sess.SendChecked(sessionCtx, s.env.Export()...) if err != nil { return 1, err } // if s.options.GlobalOptions.Verbose { // sess.SendChecked(sessionCtx, "set -xv") // } if yes, _ := util.Exists(s.HostPath("init.sh")); yes { exit, _, err := sess.SendChecked(sessionCtx, fmt.Sprintf(`source "%s"`, s.GuestPath("init.sh"))) if exit != 0 { return exit, errors.New("Ack!") } if err != nil { return 1, err } } if yes, _ := util.Exists(s.HostPath("run.sh")); yes { exit, _, err := sess.SendChecked(sessionCtx, fmt.Sprintf(`source "%s" < /dev/null`, s.GuestPath("run.sh"))) return exit, err } return 0, nil }
// guessAuthToken will attempt to read from the token store location if // no auth token was provided func guessAuthToken(c util.Settings, e *util.Environment, authTokenStore string) string { token, _ := c.GlobalString("auth-token") if token != "" { return token } if foundToken, _ := util.Exists(authTokenStore); !foundToken { return "" } tokenBytes, err := ioutil.ReadFile(authTokenStore) if err != nil { util.RootLogger().WithField("Logger", "Options").Errorln(err) return "" } return strings.TrimSpace(string(tokenBytes)) }
// LocalSymlink makes sure we have an easy to use local symlink func (s *ExternalStep) LocalSymlink() { name := strings.Replace(s.DisplayName(), " ", "-", -1) checkName := fmt.Sprintf("step-%s", name) checkPath := s.options.HostPath(checkName) counter := 1 newPath := checkPath for { already, _ := util.Exists(newPath) if !already { os.Symlink(s.HostPath(), newPath) break } newPath = fmt.Sprintf("%s-%d", checkPath, counter) counter++ } }
func findYaml(searchDirs []string) (string, error) { possibleYaml := []string{"ewok.yml", "wercker.yml", ".wercker.yml"} for _, v := range searchDirs { for _, y := range possibleYaml { possibleYaml := path.Join(v, y) ymlExists, err := util.Exists(possibleYaml) if err != nil { return "", err } if !ymlExists { continue } return possibleYaml, nil } } return "", fmt.Errorf("No wercker.yml found") }
func cmdPull(c *cli.Context, options *core.PullOptions, dockerOptions *dockerlocal.DockerOptions) error { soft := NewSoftExit(options.GlobalOptions) logger := util.RootLogger().WithField("Logger", "Main") if options.Debug { DumpOptions(options) } client := api.NewAPIClient(&api.APIOptions{ BaseURL: options.GlobalOptions.BaseURL, AuthToken: options.GlobalOptions.AuthToken, }) var buildID string if core.IsBuildID(options.Repository) { buildID = options.Repository } else { username, applicationName, err := core.ParseApplicationID(options.Repository) if err != nil { return soft.Exit(err) } logger.Println("Fetching build information for application", options.Repository) opts := &api.GetBuildsOptions{ Limit: 1, Branch: options.Branch, Result: options.Result, Status: "finished", Stack: 5, } builds, err := client.GetBuilds(username, applicationName, opts) if err != nil { return soft.Exit(err) } if len(builds) != 1 { return soft.Exit(errors.New("No finished builds found for this application")) } buildID = builds[0].ID } if buildID == "" { return soft.Exit(errors.New("Unable to parse argument as application or build-id")) } logger.Println("Downloading Docker repository for build", buildID) if !options.Force { outputExists, err := util.Exists(options.Output) if err != nil { logger.WithField("Error", err).Error("Unable to create output file") return soft.Exit(err) } if outputExists { return soft.Exit(errors.New("The file repository.tar already exists. Delete it, or run again with -f")) } } file, err := os.Create(options.Output) if err != nil { logger.WithField("Error", err).Error("Unable to create output file") return soft.Exit(err) } repository, err := client.GetDockerRepository(buildID) if err != nil { os.Remove(file.Name()) return soft.Exit(err) } defer repository.Content.Close() // Diagram of the various readers/writers // repository <-- tee <-- s <-- [io.Copy] --> file // | // +--> hash *Legend: --> == write, <-- == read counter := util.NewCounterReader(repository.Content) stopEmit := emitProgress(counter, repository.Size, util.NewRawLogger()) hash := sha256.New() tee := io.TeeReader(counter, hash) s := snappystream.NewReader(tee, true) _, err = io.Copy(file, s) if err != nil { logger.WithField("Error", err).Error("Unable to copy data from URL to file") os.Remove(file.Name()) return soft.Exit(err) } stopEmit <- true logger.Println("Download complete") calculatedHash := hex.EncodeToString(hash.Sum(nil)) if calculatedHash != repository.Sha256 { return soft.Exit(fmt.Errorf("Calculated hash did not match provided hash (calculated: %s ; expected: %s)", calculatedHash, repository.Sha256)) } if options.Load { _, err = file.Seek(0, 0) if err != nil { logger.WithField("Error", err).Error("Unable to reset seeker") return soft.Exit(err) } dockerClient, err := dockerlocal.NewDockerClient(dockerOptions) if err != nil { logger.WithField("Error", err).Error("Unable to create Docker client") return soft.Exit(err) } logger.Println("Importing into Docker") importImageOptions := docker.LoadImageOptions{InputStream: file} err = dockerClient.LoadImage(importImageOptions) if err != nil { logger.WithField("Error", err).Error("Unable to load image") return soft.Exit(err) } logger.Println("Finished importing into Docker") } return nil }
// Fetch grabs the Step content (or calls FetchScript for script steps). func (s *ExternalStep) Fetch() (string, error) { // NOTE(termie): polymorphism based on kind, we could probably do something // with interfaces here, but this is okay for now if s.IsScript() { return s.FetchScript() } stepPath := filepath.Join(s.options.StepPath(), s.CachedName()) stepExists, err := util.Exists(stepPath) if err != nil { return "", err } if !stepExists { // If we don't have a url already if s.url == "" { // Grab the info about the step from the api // TODO(termie): probably don't need these in global options? apiOptions := api.APIOptions{ BaseURL: s.options.GlobalOptions.BaseURL, AuthToken: s.options.GlobalOptions.AuthToken, } client := api.NewAPIClient(&apiOptions) stepInfo, err := client.GetStepVersion(s.Owner(), s.Name(), s.Version()) if err != nil { if apiErr, ok := err.(*api.APIError); ok && apiErr.StatusCode == 404 { return "", fmt.Errorf("The step \"%s\" was not found", s.ID()) } return "", err } s.url = stepInfo.TarballURL } // If we have a file uri let's just copytree it. if strings.HasPrefix(s.url, "file:///") { if s.options.EnableDevSteps { localPath := s.url[len("file://"):] err = shutil.CopyTree(localPath, stepPath, nil) if err != nil { return "", err } } else { return "", fmt.Errorf("Dev mode is not enabled so refusing to copy local file urls: %s", s.url) } } else { // Grab the tarball and util.Untargzip it resp, err := util.FetchTarball(s.url) if err != nil { return "", err } // Assuming we have a gzip'd tarball at this point err = util.Untargzip(stepPath, resp.Body) if err != nil { return "", err } } } hostStepPath := s.HostPath() err = shutil.CopyTree(stepPath, hostStepPath, nil) if err != nil { return "", nil } // Now that we have the code, load any step config we might find desc, err := ReadStepDesc(s.HostPath("wercker-step.yml")) if err != nil && !os.IsNotExist(err) { // TODO(termie): Log an error instead of printing s.logger.Println("ERROR: Reading wercker-step.yml:", err) } if err == nil { s.stepDesc = desc } return hostStepPath, nil }
// EnsureCode makes sure the code is in the ProjectDir. // NOTE(termie): When launched by kiddie-pool the ProjectPath will be // set to the location where grappler checked out the code and the copy // will be a little superfluous, but in the case where this is being // run in Single Player Mode this copy is necessary to avoid screwing // with the local dir. func (p *Runner) EnsureCode() (string, error) { projectDir := p.ProjectDir() if p.options.DirectMount { return projectDir, nil } // If the target is a tarball feetch and build that if p.options.ProjectURL != "" { resp, err := util.FetchTarball(p.options.ProjectURL) if err != nil { return projectDir, err } err = util.Untargzip(projectDir, resp.Body) if err != nil { return projectDir, err } } else { // We were pointed at a path with ProjectPath, copy it to projectDir ignoreFiles := []string{ p.options.BuildPath(), p.options.ProjectDownloadPath(), p.options.StepPath(), p.options.ContainerPath(), p.options.CachePath(), } var gitIgnoreRules *ignore.GitIgnore var err error if p.options.EnableGitIgnore { gitIgnorePath := filepath.Join(p.options.ProjectPath, ".gitignore") if hasGitIgnore, _ := util.Exists(gitIgnorePath); hasGitIgnore { gitIgnoreRules, err = ignore.CompileIgnoreFile(gitIgnorePath) if err != nil { return projectDir, err } } } // Make sure we don't accidentally recurse or copy extra files ignoreFunc := func(src string, files []os.FileInfo) []string { ignores := []string{} for _, file := range files { abspath, err := filepath.Abs(filepath.Join(src, file.Name())) if err != nil { // Something went sufficiently wrong panic(err) } if util.ContainsString(ignoreFiles, abspath) { ignores = append(ignores, file.Name()) } else if gitIgnoreRules != nil && gitIgnoreRules.MatchesPath(file.Name()) { ignores = append(ignores, file.Name()) } } return ignores } copyOpts := &shutil.CopyTreeOptions{Ignore: ignoreFunc, CopyFunction: shutil.Copy} os.Rename(projectDir, fmt.Sprintf("%s-%s", projectDir, uuid.NewRandom().String())) err = shutil.CopyTree(p.options.ProjectPath, projectDir, copyOpts) if err != nil { return projectDir, err } } return projectDir, nil }