func (r *TarReader) Read() (Release, error) {
	var release Release

	downloadPath, err := r.downloader.Download(r.url)
	if err != nil {
		return release, bosherr.WrapError(err, "Downloading release")
	}

	r.downloadPath = downloadPath

	extractPath, err := r.extractor.Extract(r.downloadPath)
	if err != nil {
		cleanUpErr := r.downloader.CleanUp(r.downloadPath)
		if cleanUpErr != nil {
			r.logger.Debug(tarReaderLogTag,
				"Failed to clean up downloaded release %v", cleanUpErr)
		}

		return release, bosherr.WrapError(err, "Extracting release")
	}

	r.extractPath = extractPath

	manifestPath := filepath.Join(r.extractPath, "release.MF")

	manifest, err := bprelman.NewManifestFromPath(manifestPath, r.fs)
	if err != nil {
		closeErr := r.Close()
		if closeErr != nil {
			r.logger.Debug(tarReaderLogTag,
				"Failed to close release %v", closeErr)
		}

		return release, bosherr.WrapError(err, "Building manifest")
	}

	r.logger.Debug(tarReaderLogTag, "Done building manifest %#v", manifest)

	release.populateFromManifest(manifest)

	r.populateReleaseTarPaths(&release)

	return release, nil
}
func (r DirReader) Read() (Release, error) {
	var release Release

	// e.g. room101-0+dev.16.yml
	manifestFileName := r.releaseName + "-" + r.releaseVersion + ".yml"

	// bosh_cli now places release manifests into sub-directories named after a release
	oldManifestPath := filepath.Join(r.dir, "dev_releases", manifestFileName)
	migratedManifestPath := filepath.Join(r.dir, "dev_releases", r.releaseName, manifestFileName)

	manifestPath := r.pathThatExistsOrEmpty(oldManifestPath, migratedManifestPath)
	if len(manifestPath) == 0 {
		return release, bosherr.Errorf(
			"Manifest not found at '%s' or '%s'",
			oldManifestPath,
			migratedManifestPath,
		)
	}

	manifest, err := bprelman.NewManifestFromPath(manifestPath, r.fs)
	if err != nil {
		closeErr := r.Close()
		if closeErr != nil {
			r.logger.Debug(dirReaderLogTag, "Failed to close release %v", closeErr)
		}

		return release, bosherr.WrapError(err, "Building manifest")
	}

	r.logger.Debug(dirReaderLogTag, "Done building manifest %#v", manifest)

	release.populateFromManifest(manifest)

	r.populateReleaseTarPaths(&release)

	return release, nil
}
func (r ReleaseDir) findReleaseManifestPaths(releaseDir string) (map[string]bprelman.Manifest, error) {
	manifests := map[string]bprelman.Manifest{}

	baseDir := releaseDir

	// todo Once bosh release is a separate repo this needs to go
	if r.fs.FileExists(filepath.Join(releaseDir, "bosh-director")) {
		baseDir = filepath.Join(releaseDir, "release")
	}

	releaseMatches, err := r.fs.Glob(filepath.Join(baseDir, "releases/**/*"))
	if err != nil {
		return nil, bosherr.WrapError(err, "Globbing releases/**/* directory")
	}

	nonDirReleaseMatches, err := r.fs.Glob(filepath.Join(baseDir, "releases/*"))
	if err != nil {
		return nil, bosherr.WrapError(err, "Globbing releases/* directory")
	}

	for _, releaseMatch := range nonDirReleaseMatches {
		releaseMatches = append(releaseMatches, releaseMatch)
	}

	r.logger.Debug(r.logTag, "Found '%d' release metadata files", len(releaseMatches))

	// Release manifest file names will be unique even if they are in release sub-directories
	// e.g. relname-1.yml, relname-0.567.yml
	lookedAtFileNames := map[string]struct{}{}

	for _, releaseMatch := range releaseMatches {
		baseFileName := filepath.Base(releaseMatch)

		// index.yml is not a release manifest or it's not a yml file
		if baseFileName == "index.yml" || filepath.Ext(baseFileName) != ".yml" {
			continue
		}

		// releaseMatches and nonDirReleaseMatches will contain dup items
		if _, ok := lookedAtFileNames[baseFileName]; ok {
			r.logger.Debug(r.logTag, "Skipping over dup release match '%s'", releaseMatch)
			continue
		}

		manifest, err := bprelman.NewManifestFromPath(releaseMatch, r.fs)
		if err != nil {
			// todo should this failure be shown somewhere?
			// e.g. https://github.com/cloudfoundry/bosh/blob/master/release/releases/bosh-1.yml
			r.logger.Debug(r.logTag, "Failed building manifest '%s'", releaseMatch)
			continue
		}

		lookedAtFileNames[baseFileName] = struct{}{}

		r.logger.Debug(r.logTag, "Reading manifest '%s'", releaseMatch)

		manifests[releaseMatch] = manifest
	}

	return manifests, nil
}