示例#1
0
文件: main.go 项目: rlugojr/wercker
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
}
示例#2
0
文件: step.go 项目: hughker/wercker
// 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
}