Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1122)

Unified Diff: vpython/cipd/cipd.go

Issue 2699223002: vpython: Add CIPD support. (Closed)
Patch Set: comments, path => name Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « vpython/api/vpython/spec.pb.go ('k') | vpython/spec/load_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: vpython/cipd/cipd.go
diff --git a/vpython/cipd/cipd.go b/vpython/cipd/cipd.go
new file mode 100644
index 0000000000000000000000000000000000000000..2b372528645e4e5934c5d1d22b774d39af584d39
--- /dev/null
+++ b/vpython/cipd/cipd.go
@@ -0,0 +1,155 @@
+// Copyright 2017 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package cipd
+
+import (
+ "github.com/luci/luci-go/vpython/api/vpython"
+ "github.com/luci/luci-go/vpython/venv"
+
+ "github.com/luci/luci-go/cipd/client/cipd"
+ "github.com/luci/luci-go/cipd/client/cipd/common"
+ "github.com/luci/luci-go/cipd/client/cipd/ensure"
+ "github.com/luci/luci-go/common/errors"
+ "github.com/luci/luci-go/common/logging"
+
+ "golang.org/x/net/context"
+)
+
+// PackageLoader is an implementation of venv.PackageLoader that uses the
+// CIPD service to fetch packages.
+//
+// Packages that use the CIPD loader use the CIPD package name as their Path
+// and a CIPD version/tag/ref as their Version.
+type PackageLoader struct {
+ // Options are additional client options to use when generating CIPD clients.
+ Options cipd.ClientOptions
+}
+
+var _ venv.PackageLoader = (*PackageLoader)(nil)
+
+// Resolve implements venv.PackageLoader.
+//
+// The resulting packages slice will be updated in-place with the resolved
+// package name and instance ID.
+func (pl *PackageLoader) Resolve(c context.Context, root string, packages []*vpython.Spec_Package) error {
+ if len(packages) == 0 {
+ return nil
+ }
+
+ logging.Debugf(c, "Resolving CIPD packages:")
+ pslice := make(ensure.PackageSlice, len(packages))
+ for i, pkg := range packages {
+ pslice[i] = ensure.PackageDef{
+ PackageTemplate: pkg.Name,
+ UnresolvedVersion: pkg.Version,
+ }
+
+ logging.Debugf(c, "\tUnresolved package: %s", pslice[i])
+ }
+
+ ef := ensure.File{
+ PackagesBySubdir: map[string]ensure.PackageSlice{"": pslice},
+ }
+
+ // Generate a CIPD client. Use the supplied root.
+ opts := pl.Options
+ opts.Root = root
+ client, err := cipd.NewClient(opts)
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to generate CIPD client").Err()
+ }
+
+ // Start a CIPD client batch.
+ client.BeginBatch(c)
+ defer client.EndBatch(c)
+
+ // Resolve our ensure file.
+ resolved, err := ef.Resolve(func(pkg, vers string) (common.Pin, error) {
+ pin, err := client.ResolveVersion(c, pkg, vers)
+ if err != nil {
+ return pin, errors.Annotate(err).Reason("failed to resolve package %(package)q at version %(version)q").
+ D("package", pkg).
+ D("version", vers).
+ Err()
+ }
+
+ logging.Fields{
+ "package": pkg,
+ "version": vers,
+ }.Debugf(c, "Resolved package to: %s", pin)
+ return pin, nil
+ })
+ if err != nil {
+ return err
+ }
+
+ // Write the results to "packages". All of them should have been installed
+ // into the root subdir.
+ for i, pkg := range resolved.PackagesBySubdir[""] {
+ packages[i].Name = pkg.PackageName
+ packages[i].Version = pkg.InstanceID
+ }
+ return nil
+}
+
+// Ensure implement venv.PackageLoader.
+//
+// The packages must be valid (PackageIsComplete). If they aren't, Ensure will
+// panic.
+//
+// The CIPD client that is used for the operation is generated from the supplied
+// options, opts.
+func (pl *PackageLoader) Ensure(c context.Context, root string, packages []*vpython.Spec_Package) error {
+ pins, err := packagesToPins(packages)
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to convert packages to CIPD pins").Err()
+ }
+ pinSlice := common.PinSliceBySubdir{
+ "": pins,
+ }
+
+ // Generate a CIPD client. Use the supplied root.
+ opts := pl.Options
+ opts.Root = root
+ client, err := cipd.NewClient(opts)
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to generate CIPD client").Err()
+ }
+
+ // Start a CIPD client batch.
+ client.BeginBatch(c)
+ defer client.EndBatch(c)
+
+ actionMap, err := client.EnsurePackages(c, pinSlice, false)
+ if err != nil {
+ return errors.Annotate(err).Reason("failed to install CIPD packages").Err()
+ }
+ if len(actionMap) > 0 {
+ errorCount := 0
+ for root, action := range actionMap {
+ errorCount += len(action.Errors)
+ for _, err := range action.Errors {
+ logging.Errorf(c, "CIPD root %q action %q for pin %q encountered error: %s", root, err.Action, err.Pin, err)
+ }
+ }
+ if errorCount > 0 {
+ return errors.Reason("CIPD package installation encountered %(count)d error(s)").
+ D("count", errorCount).
+ Err()
+ }
+ }
+ return nil
+}
+
+func packagesToPins(packages []*vpython.Spec_Package) ([]common.Pin, error) {
+ pins := make([]common.Pin, len(packages))
+ for i, pkg := range packages {
+ pins[i] = common.Pin{
+ PackageName: pkg.Name,
+ InstanceID: pkg.Version,
+ }
+ }
+ return pins, nil
+}
« no previous file with comments | « vpython/api/vpython/spec.pb.go ('k') | vpython/spec/load_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698