Esempio n. 1
0
// packageNameDigest returns a filename to use for naming a package directory in
// the file system. Using package names as is can introduce problems on file
// systems with path length limits (on Windows in particular). Returns stripped
// SHA1 of the whole package name on Windows. On Linux\Mac also prepends last
// two components of the package name (for better readability of .cipd/*
// directory).
func packageNameDigest(pkg string) string {
	// Be paranoid.
	err := common.ValidatePackageName(pkg)
	if err != nil {
		panic(err.Error())
	}

	// Grab stripped SHA1 of the full package name.
	digest := sha1.Sum([]byte(pkg))
	hash := base64.URLEncoding.EncodeToString(digest[:])[:10]

	// On Windows paths are restricted to 260 chars, so every byte counts.
	if runtime.GOOS == "windows" {
		return hash
	}

	// On Posix file paths are not so restricted, so we can make names more
	// readable. Grab last <= 2 components of the package path and join them with
	// the digest.
	chunks := strings.Split(pkg, "/")
	if len(chunks) > 2 {
		chunks = chunks[len(chunks)-2:]
	}
	chunks = append(chunks, hash)
	return strings.Join(chunks, "_")
}
Esempio n. 2
0
func aclEndpoint(packagePath string) (string, error) {
	if err := common.ValidatePackageName(packagePath); err != nil {
		return "", err
	}
	params := url.Values{}
	params.Add("package_path", packagePath)
	return "repo/v1/acl?" + params.Encode(), nil
}
Esempio n. 3
0
func refEndpoint(packageName string, ref string) (string, error) {
	if err := common.ValidatePackageName(packageName); err != nil {
		return "", err
	}
	if err := common.ValidatePackageRef(ref); err != nil {
		return "", err
	}
	params := url.Values{}
	params.Add("package_name", packageName)
	params.Add("ref", ref)
	return "repo/v1/ref?" + params.Encode(), nil
}
Esempio n. 4
0
func (client *clientImpl) ResolveVersion(packageName, version string) (common.Pin, error) {
	if err := common.ValidatePackageName(packageName); err != nil {
		return common.Pin{}, err
	}
	// Is it instance ID already? Don't bother calling the backend.
	if common.ValidateInstanceID(version) == nil {
		return common.Pin{PackageName: packageName, InstanceID: version}, nil
	}
	if err := common.ValidateInstanceVersion(version); err != nil {
		return common.Pin{}, err
	}
	client.Logger.Debugf("cipd: resolving version %q of %q...", version, packageName)
	return client.remote.resolveVersion(packageName, version)
}
Esempio n. 5
0
func (client *clientImpl) ProcessEnsureFile(r io.Reader) ([]common.Pin, error) {
	lineNo := 0
	makeError := func(msg string) error {
		return fmt.Errorf("Failed to parse desired state (line %d): %s", lineNo, msg)
	}

	out := []common.Pin{}
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		lineNo++

		// Split each line into words, ignore white space.
		tokens := []string{}
		for _, chunk := range strings.Split(scanner.Text(), " ") {
			chunk = strings.TrimSpace(chunk)
			if chunk != "" {
				tokens = append(tokens, chunk)
			}
		}

		// Skip empty lines or lines starting with '#'.
		if len(tokens) == 0 || tokens[0][0] == '#' {
			continue
		}

		// Each line has a format "<package name> <version>".
		if len(tokens) != 2 {
			return nil, makeError("expecting '<package name> <version>' line")
		}
		err := common.ValidatePackageName(tokens[0])
		if err != nil {
			return nil, makeError(err.Error())
		}
		err = common.ValidateInstanceVersion(tokens[1])
		if err != nil {
			return nil, makeError(err.Error())
		}

		// Good enough.
		pin, err := client.ResolveVersion(tokens[0], tokens[1])
		if err != nil {
			return nil, err
		}
		out = append(out, pin)
	}

	return out, nil
}
Esempio n. 6
0
func (r *remoteImpl) resolveVersion(packageName, version string) (pin common.Pin, err error) {
	if err = common.ValidatePackageName(packageName); err != nil {
		return
	}
	if err = common.ValidateInstanceVersion(version); err != nil {
		return
	}
	var reply struct {
		Status       string `json:"status"`
		ErrorMessage string `json:"error_message"`
		InstanceID   string `json:"instance_id"`
	}
	params := url.Values{}
	params.Add("package_name", packageName)
	params.Add("version", version)
	err = r.makeRequest("repo/v1/instance/resolve?"+params.Encode(), "GET", nil, &reply)
	if err != nil {
		return
	}
	switch reply.Status {
	case "SUCCESS":
		if common.ValidateInstanceID(reply.InstanceID) != nil {
			err = fmt.Errorf("Backend returned invalid instance ID: %s", reply.InstanceID)
		} else {
			pin = common.Pin{PackageName: packageName, InstanceID: reply.InstanceID}
		}
	case "PACKAGE_NOT_FOUND":
		err = fmt.Errorf("Package '%s' is not registered", packageName)
	case "INSTANCE_NOT_FOUND":
		err = fmt.Errorf("Package '%s' doesn't have instance with version '%s'", packageName, version)
	case "AMBIGUOUS_VERSION":
		err = fmt.Errorf("More than one instance of package '%s' match version '%s'", packageName, version)
	case "ERROR":
		err = errors.New(reply.ErrorMessage)
	default:
		err = fmt.Errorf("Unexpected backend response: %s", reply.Status)
	}
	return
}
Esempio n. 7
0
func (d *deployerImpl) RemoveDeployed(packageName string) error {
	d.logger.Infof("Removing %s from %s", packageName, d.fs.Root())
	if err := common.ValidatePackageName(packageName); err != nil {
		return err
	}
	pkgPath := d.packagePath(packageName)

	// Read the manifest of the currently installed version.
	manifest := Manifest{}
	currentID, err := d.getCurrentInstanceID(pkgPath)
	if err == nil && currentID != "" {
		manifest, err = d.readManifest(filepath.Join(pkgPath, currentID))
	}

	// Warn, but continue with removal anyway. EnsureDirectoryGone call below
	// will nuke everything (even if it's half broken).
	if err != nil {
		d.logger.Warningf("Package %s is in a broken state: %s", packageName, err)
	} else {
		d.removeFromSiteRoot(manifest.Files)
	}
	return d.fs.EnsureDirectoryGone(pkgPath)
}
Esempio n. 8
0
// BuildInstance builds a new package instance for package named opts.PackageName
// by archiving input files (passed via opts.Input). The final binary is written
// to opts.Output. Some output may be written even if BuildInstance eventually
// returns an error.
func BuildInstance(opts BuildInstanceOptions) error {
	if opts.Logger == nil {
		opts.Logger = logging.Null()
	}
	err := common.ValidatePackageName(opts.PackageName)
	if err != nil {
		return err
	}

	// Make sure no files are written to package service directory.
	for _, f := range opts.Input {
		if strings.HasPrefix(f.Name(), packageServiceDir+"/") {
			return fmt.Errorf("Can't write to %s: %s", packageServiceDir, f.Name())
		}
	}

	// Generate the manifest file, add to the list of input files.
	manifestFile, err := makeManifestFile(opts)
	if err != nil {
		return err
	}
	files := append(opts.Input, manifestFile)

	// Make sure filenames are unique.
	seenNames := make(map[string]struct{}, len(files))
	for _, f := range files {
		_, seen := seenNames[f.Name()]
		if seen {
			return fmt.Errorf("File %s is provided twice", f.Name())
		}
		seenNames[f.Name()] = struct{}{}
	}

	// Write the final zip file.
	return zipInputFiles(files, opts.Output, opts.Logger)
}
Esempio n. 9
0
// LoadPackageDef loads package definition from a YAML source code. In
// substitutes %{...} strings in the definition with corresponding values
// from 'vars' map.
func LoadPackageDef(r io.Reader, vars map[string]string) (PackageDef, error) {
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return PackageDef{}, err
	}

	out := PackageDef{}
	if err = yaml.Unmarshal(data, &out); err != nil {
		return PackageDef{}, err
	}

	// Substitute variables in all strings.
	for _, str := range out.strings() {
		*str, err = subVars(*str, vars)
		if err != nil {
			return PackageDef{}, err
		}
	}

	// Validate global package properties.
	if err = common.ValidatePackageName(out.Package); err != nil {
		return PackageDef{}, err
	}
	if err = ValidateInstallMode(out.InstallMode); err != nil {
		return PackageDef{}, err
	}

	versionFile := ""
	for i, chunk := range out.Data {
		// Make sure 'dir' and 'file' etc. aren't used together.
		has := []string{}
		if chunk.File != "" {
			has = append(has, "file")
		}
		if chunk.VersionFile != "" {
			has = append(has, "version_file")
		}
		if chunk.Dir != "" {
			has = append(has, "dir")
		}
		if len(has) == 0 {
			return out, fmt.Errorf("files entry #%d needs 'file', 'dir' or 'version_file' key", i)
		}
		if len(has) != 1 {
			return out, fmt.Errorf("files entry #%d should have only one key, got %q", i, has)
		}
		//'version_file' can appear only once, it must be a clean relative path.
		if chunk.VersionFile != "" {
			if versionFile != "" {
				return out, fmt.Errorf("'version_file' entry can be used only once")
			}
			versionFile = chunk.VersionFile
			if !isCleanSlashPath(versionFile) {
				return out, fmt.Errorf("'version_file' must be a path relative to the package root: %s", versionFile)
			}
		}
	}

	// Default 'root' to a directory with the package def file.
	if out.Root == "" {
		out.Root = "."
	}
	return out, nil
}