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

Side by Side Diff: vpython/cipd/cipd.go

Issue 2699223002: vpython: Add CIPD support. (Closed)
Patch Set: Created 3 years, 10 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 unified diff | Download patch
« no previous file with comments | « no previous file | vpython/cipd/cipd_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2017 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package cipd
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11
12 "github.com/luci/luci-go/vpython/api/env"
13 "github.com/luci/luci-go/vpython/venv"
14
15 "github.com/luci/luci-go/cipd/client/cipd"
16 "github.com/luci/luci-go/cipd/client/cipd/common"
17 "github.com/luci/luci-go/cipd/client/cipd/ensure"
18 "github.com/luci/luci-go/common/errors"
19 "github.com/luci/luci-go/common/logging"
20
21 "golang.org/x/net/context"
22 )
23
24 // PackageLoader is an implementation of venv.PackageLoader that uses the
25 // CIPD service to fetch packages.
26 //
27 // Packages that use the CIPD loader use the CIPD package name as their Path
28 // and a CIPD version/tag/ref as their Version.
29 type PackageLoader struct {
30 // Options are additional client options to use when generating CIPD cli ents.
31 Options cipd.ClientOptions
32 }
33
34 var _ venv.PackageLoader = (*PackageLoader)(nil)
35
36 // Resolve implements venv.PackageLoader.
37 //
38 // The resulting packages slice will be updated in-place with the resolves
iannucci 2017/02/23 01:06:56 resolved
dnj 2017/03/11 16:47:24 Done.
39 // package name and instance ID.
40 func (pl *PackageLoader) Resolve(c context.Context, root string, packages []*env .Spec_Package) error {
41 if len(packages) == 0 {
42 return nil
43 }
44
45 var ensureFile bytes.Buffer
46 if err := writeEnsureFile(&ensureFile, packages); err != nil {
47 return errors.Annotate(err).Reason("failed to generate manifest" ).Err()
48 }
49
50 // Generate a CIPD client. Use the supplied root.
51 opts := pl.Options
52 opts.Root = root
53 client, err := cipd.NewClient(opts)
54 if err != nil {
55 return errors.Annotate(err).Reason("failed to generate CIPD clie nt").Err()
56 }
57
58 // Start a CIPD client batch.
59 client.BeginBatch(c)
60 defer client.EndBatch(c)
61
62 // Parse and resolve the CIPD ensure file.
63 logging.Debugf(c, "Resolving CIPD manifest:\n%s", ensureFile.Bytes())
64 ef, err := ensure.ParseFile(&ensureFile)
iannucci 2017/02/23 01:06:56 could just make an ensure.File directly rather tha
dnj 2017/03/11 16:47:24 Oh neat! I don't think this was exported the first
65 if err != nil {
66 return errors.Annotate(err).Reason("failed to process ensure fil e").Err()
67 }
68
69 resolved, err := ef.Resolve(func(pkg, vers string) (common.Pin, error) {
70 return client.ResolveVersion(c, pkg, vers)
71 })
72 if err != nil {
73 return errors.Annotate(err).Reason("failed to resolve ensure fil e").Err()
74 }
75
76 // Write the results to "packages". All of them should have been install ed
77 // into the root subdir.
78 for i, pkg := range resolved.PackagesBySubdir[""] {
79 packages[i].Path = pkg.PackageName
iannucci 2017/02/23 01:06:56 why is it packages[i].Path? Shouldn't it be Name o
dnj 2017/03/11 16:47:24 In the protobuf, "package" is an intentionally gen
80 packages[i].Version = pkg.InstanceID
81 }
82 return nil
83 }
84
85 // Ensure implement venv.PackageLoader.
86 //
87 // The packages must be valid (PackageIsComplete). If they aren't, Ensure will
88 // panic.
89 //
90 // The CIPD client that is used for the operation is generated from the supplied
91 // options, opts.
92 func (pl *PackageLoader) Ensure(c context.Context, root string, packages []*env. Spec_Package) error {
93 pins, err := packagesToPins(packages)
94 if err != nil {
95 return errors.Annotate(err).Reason("failed to convert packages t o CIPD pins").Err()
96 }
97 pinSlice := common.PinSliceBySubdir{
98 "": pins,
99 }
100
101 // Generate a CIPD client. Use the supplied root.
102 opts := pl.Options
103 opts.Root = root
104 client, err := cipd.NewClient(opts)
105 if err != nil {
106 return errors.Annotate(err).Reason("failed to generate CIPD clie nt").Err()
107 }
108
109 // Start a CIPD client batch.
110 client.BeginBatch(c)
111 defer client.EndBatch(c)
112
113 actionMap, err := client.EnsurePackages(c, pinSlice, false)
114 if err != nil {
115 return errors.Annotate(err).Reason("failed to install CIPD packa ges").Err()
116 }
117 if len(actionMap) > 0 {
118 errorCount := 0
119 for root, action := range actionMap {
120 errorCount += len(action.Errors)
121 for _, err := range action.Errors {
122 logging.Errorf(c, "CIPD root %q action %q for pi n %q encountered error: %s", root, err.Action, err.Pin, err)
123 }
124 }
125 if errorCount > 0 {
126 return errors.Reason("CIPD package installation encounte red %(count)d error(s)").
127 D("count", errorCount).
128 Err()
129 }
130 }
131 return nil
132 }
133
134 func writeEnsureFile(out io.Writer, packages []*env.Spec_Package) error {
135 for _, pkg := range packages {
136 if err := validatePackage(pkg); err != nil {
137 panic(errors.Annotate(err).Reason("invalid CIPD package" ).Err())
138 }
139 if _, err := fmt.Fprintf(out, "%s %s\n", pkg.Path, pkg.Version); err != nil {
140 return errors.Annotate(err).Reason("failed to write mani fest line").Err()
141 }
142 }
143 return nil
144 }
145
146 // validatePackage returns an error if the package does not have all of the
147 // required fields to describe a CIPD package.
148 func validatePackage(pkg *env.Spec_Package) error {
iannucci 2017/02/23 01:06:56 tbh... Ensure will do this better anyway: https://
dnj 2017/03/11 16:47:24 Done.
149 switch {
150 case pkg.Path == "":
151 return errors.New("package must have a path")
152 case pkg.Version == "":
153 return errors.New("package must have a version")
154 default:
155 return nil
156 }
157 }
158
159 func packagesToPins(packages []*env.Spec_Package) ([]common.Pin, error) {
160 pins := make([]common.Pin, len(packages))
161 for i, pkg := range packages {
162 pins[i] = common.Pin{
163 PackageName: pkg.Path,
164 InstanceID: pkg.Version,
165 }
166 }
167 return pins, nil
168 }
OLDNEW
« no previous file with comments | « no previous file | vpython/cipd/cipd_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698