Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |