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

Unified Diff: cipd/client/cipd/client.go

Issue 2661313003: [cipd] Enable subdirs functionality. (Closed)
Patch Set: Make golint happy Created 3 years, 11 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 | « no previous file | cipd/client/cipd/client_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cipd/client/cipd/client.go
diff --git a/cipd/client/cipd/client.go b/cipd/client/cipd/client.go
index 5bd14751674cea0fd4d1237c657e05661881f7c5..e1f988176a7303ee5e518ac131f11feac2504a1e 100644
--- a/cipd/client/cipd/client.go
+++ b/cipd/client/cipd/client.go
@@ -47,6 +47,7 @@ import (
"golang.org/x/net/context"
"github.com/luci/luci-go/common/clock"
+ "github.com/luci/luci-go/common/data/stringset"
"github.com/luci/luci-go/common/errors"
"github.com/luci/luci-go/common/logging"
@@ -107,7 +108,7 @@ var (
var (
// UserAgent is HTTP user agent string for CIPD client.
- UserAgent = "cipd 1.4.0"
+ UserAgent = "cipd 1.5.0"
)
func init() {
@@ -231,6 +232,62 @@ type Counter struct {
UpdatedTS UnixTime `json:"updated_ts"`
}
+// ActionMap is a map of subdir to the Actions which will occur within it.
+type ActionMap map[string]*Actions
+
+// LoopOrdered loops over the ActionMap in sorted order (by subdir).
+func (am ActionMap) LoopOrdered(cb func(subdir string, actions *Actions)) {
+ subdirs := make(sort.StringSlice, 0, len(am))
+ for subdir := range am {
+ subdirs = append(subdirs, subdir)
+ }
+ subdirs.Sort()
+ for _, subdir := range subdirs {
+ cb(subdir, am[subdir])
+ }
+}
+
+// Log prints the pending action to the logger installed in ctx.
+func (am ActionMap) Log(ctx context.Context) {
+ keys := make([]string, 0, len(am))
+ for key := range am {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ for _, subdir := range keys {
+ actions := am[subdir]
+
+ if subdir == "" {
+ if len(keys) > 1 {
+ logging.Infof(ctx, "In root:")
+ }
+ } else {
+ logging.Infof(ctx, "In subdir %q:", subdir)
+ }
+
+ if len(actions.ToInstall) != 0 {
+ logging.Infof(ctx, " to install:")
+ for _, pin := range actions.ToInstall {
+ logging.Infof(ctx, " %s", pin)
+ }
+ }
+ if len(actions.ToUpdate) != 0 {
+ logging.Infof(ctx, " to updated:")
+ for _, pair := range actions.ToUpdate {
+ logging.Infof(ctx, " %s (%s -> %s)",
+ pair.From.PackageName, pair.From.InstanceID, pair.To.InstanceID)
+ }
+ }
+ if len(actions.ToRemove) != 0 {
+ logging.Infof(ctx, " to removed:")
+ for _, pin := range actions.ToRemove {
+ logging.Infof(ctx, " %s", pin)
+ }
+ }
+ }
+}
+
// Actions is returned by EnsurePackages.
//
// It lists pins that were attempted to be installed, updated or removed, as
@@ -387,7 +444,7 @@ type Client interface {
// struct, but won't actually perform them.
//
// If the update was only partially applied, returns both Actions and error.
- EnsurePackages(ctx context.Context, pkgs common.PinSliceBySubdir, dryRun bool) (Actions, error)
+ EnsurePackages(ctx context.Context, pkgs common.PinSliceBySubdir, dryRun bool) (ActionMap, error)
// IncrementCounter adds delta to the counter's value and updates its last
// updated timestamp.
@@ -1154,8 +1211,8 @@ func (client *clientImpl) remoteFetchInstance(ctx context.Context, pin common.Pi
}
func (client *clientImpl) FetchAndDeployInstance(ctx context.Context, subdir string, pin common.Pin) error {
- if subdir != "" {
- return common.ErrSubdirsNotYetSupported
+ if err := common.ValidateSubdir(subdir); err != nil {
+ return err
}
if err := common.ValidatePin(pin); err != nil {
return err
@@ -1197,16 +1254,11 @@ func (client *clientImpl) FetchAndDeployInstance(ctx context.Context, subdir str
return err
}
-func (client *clientImpl) EnsurePackages(ctx context.Context, allPins common.PinSliceBySubdir, dryRun bool) (actions Actions, err error) {
+func (client *clientImpl) EnsurePackages(ctx context.Context, allPins common.PinSliceBySubdir, dryRun bool) (aMap ActionMap, err error) {
if err = allPins.Validate(); err != nil {
return
}
- if err = allPins.AssertOnlyDefaultSubdir(); err != nil {
- return
- }
- pins := allPins[""]
-
client.BeginBatch(ctx)
defer client.EndBatch(ctx)
@@ -1217,82 +1269,73 @@ func (client *clientImpl) EnsurePackages(ctx context.Context, allPins common.Pin
}
// Figure out what needs to be updated and deleted, log it.
- actions = buildActionPlan(allPins, existing)
- if actions.Empty() {
+ aMap = buildActionPlan(allPins, existing)
+ if len(aMap) == 0 {
logging.Debugf(ctx, "Everything is up-to-date.")
- return actions, nil
+ return
}
// TODO(iannucci): ensure that no packages cross root boundaries
- if len(actions.ToInstall) != 0 {
- logging.Infof(ctx, "Packages to be installed:")
- for _, pin := range actions.ToInstall {
- logging.Infof(ctx, " %s", pin)
- }
- }
- if len(actions.ToUpdate) != 0 {
- logging.Infof(ctx, "Packages to be updated:")
- for _, pair := range actions.ToUpdate {
- logging.Infof(ctx, " %s (%s -> %s)",
- pair.From.PackageName, pair.From.InstanceID, pair.To.InstanceID)
- }
- }
- if len(actions.ToRemove) != 0 {
- logging.Infof(ctx, "Packages to be removed:")
- for _, pin := range actions.ToRemove {
- logging.Infof(ctx, " %s", pin)
- }
- }
+ aMap.Log(ctx)
if dryRun {
logging.Infof(ctx, "Dry run, not actually doing anything.")
- return actions, nil
+ return
}
+ hasErrors := false
+
// Remove all unneeded stuff.
- for _, pin := range actions.ToRemove {
- err = client.deployer.RemoveDeployed(ctx, "", pin.PackageName)
- if err != nil {
- logging.Errorf(ctx, "Failed to remove %s - %s", pin.PackageName, err)
- actions.Errors = append(actions.Errors, ActionError{
- Action: "remove",
- Pin: pin,
- Error: JSONError{err},
- })
+ aMap.LoopOrdered(func(subdir string, actions *Actions) {
+ for _, pin := range actions.ToRemove {
+ err = client.deployer.RemoveDeployed(ctx, subdir, pin.PackageName)
+ if err != nil {
+ logging.Errorf(ctx, "Failed to remove %s - %s (subdir %q)", pin.PackageName, err, subdir)
+ hasErrors = true
+ actions.Errors = append(actions.Errors, ActionError{
+ Action: "remove",
+ Pin: pin,
+ Error: JSONError{err},
+ })
+ }
}
- }
+ })
// Install all new and updated stuff. Install in the order specified by
// 'pins'. Order matters if multiple packages install same file.
- toDeploy := make(map[string]bool, len(actions.ToInstall)+len(actions.ToUpdate))
- for _, p := range actions.ToInstall {
- toDeploy[p.PackageName] = true
- }
- for _, pair := range actions.ToUpdate {
- toDeploy[pair.To.PackageName] = true
- }
- for _, pin := range pins {
- if !toDeploy[pin.PackageName] {
- continue
+ aMap.LoopOrdered(func(subdir string, actions *Actions) {
+ toDeploy := make(map[string]bool, len(actions.ToInstall)+len(actions.ToUpdate))
+ for _, p := range actions.ToInstall {
+ toDeploy[p.PackageName] = true
}
- err = client.FetchAndDeployInstance(ctx, "", pin)
- if err != nil {
- logging.Errorf(ctx, "Failed to install %s - %s", pin, err)
- actions.Errors = append(actions.Errors, ActionError{
- Action: "install",
- Pin: pin,
- Error: JSONError{err},
- })
+ for _, pair := range actions.ToUpdate {
+ toDeploy[pair.To.PackageName] = true
}
- }
+ for _, pin := range allPins[subdir] {
+ if !toDeploy[pin.PackageName] {
+ continue
+ }
+ err = client.FetchAndDeployInstance(ctx, subdir, pin)
+ if err != nil {
+ logging.Errorf(ctx, "Failed to install %s - %s", pin, err)
+ hasErrors = true
+ actions.Errors = append(actions.Errors, ActionError{
+ Action: "install",
+ Pin: pin,
+ Error: JSONError{err},
+ })
+ }
+ }
+ })
// Opportunistically cleanup the trash left from previous installs.
client.doBatchAwareOp(ctx, batchAwareOpCleanupTrash)
- if len(actions.Errors) == 0 {
+ if !hasErrors {
logging.Infof(ctx, "All changes applied.")
- return actions, nil
+ } else {
+ err = ErrEnsurePackagesFailed
}
- return actions, ErrEnsurePackagesFailed
+ return
}
////////////////////////////////////////////////////////////////////////////////
@@ -1372,37 +1415,68 @@ func (d deleteOnClose) Close() (err error) {
// Private stuff.
// buildActionPlan is used by EnsurePackages to figure out what to install or remove.
-func buildActionPlan(desired, existing common.PinSliceBySubdir) (a Actions) {
- if err := desired.AssertOnlyDefaultSubdir(); err != nil {
- panic(err)
- }
- if err := existing.AssertOnlyDefaultSubdir(); err != nil {
- panic(err)
- }
-
- defaultDesired := desired[""]
- defaultExisting := existing[""]
-
- // Figure out what needs to be installed or updated.
- existingMap := defaultExisting.ToMap()
- for _, d := range defaultDesired {
- if existingID, exists := existingMap[d.PackageName]; !exists {
- a.ToInstall = append(a.ToInstall, d)
- } else if existingID != d.InstanceID {
- a.ToUpdate = append(a.ToUpdate, UpdatedPin{
- From: common.Pin{PackageName: d.PackageName, InstanceID: existingID},
- To: d,
- })
- }
+func buildActionPlan(desired, existing common.PinSliceBySubdir) (aMap ActionMap) {
+ desiredSubdirs := stringset.New(len(desired))
+ for desiredSubdir := range desired {
+ desiredSubdirs.Add(desiredSubdir)
}
- // Figure out what needs to be removed.
- desiredMap := defaultDesired.ToMap()
- for _, e := range defaultExisting {
- if desiredMap[e.PackageName] == "" {
- a.ToRemove = append(a.ToRemove, e)
- }
+ existingSubdirs := stringset.New(len(existing))
+ for existingSubdir := range existing {
+ existingSubdirs.Add(existingSubdir)
}
+ aMap = ActionMap{}
+
+ // all newly added subdirs
+ desiredSubdirs.Difference(existingSubdirs).Iter(func(subdir string) bool {
+ if want := desired[subdir]; len(want) > 0 {
+ aMap[subdir] = &Actions{ToInstall: want}
+ }
+ return true
+ })
+
+ // all removed subdirs
+ existingSubdirs.Difference(desiredSubdirs).Iter(func(subdir string) bool {
+ if have := existing[subdir]; len(have) > 0 {
+ aMap[subdir] = &Actions{ToRemove: have}
+ }
+ return true
+ })
+
+ // all common subdirs
+ desiredSubdirs.Intersect(existingSubdirs).Iter(func(subdir string) bool {
+ a := Actions{}
+
+ // Figure out what needs to be installed or updated.
+ haveMap := existing[subdir].ToMap()
+ for _, want := range desired[subdir] {
+ if haveID, exists := haveMap[want.PackageName]; !exists {
+ a.ToInstall = append(a.ToInstall, want)
+ } else if haveID != want.InstanceID {
+ a.ToUpdate = append(a.ToUpdate, UpdatedPin{
+ From: common.Pin{PackageName: want.PackageName, InstanceID: haveID},
+ To: want,
+ })
+ }
+ }
+
+ // Figure out what needs to be removed.
+ wantMap := desired[subdir].ToMap()
+ for _, have := range existing[subdir] {
+ if wantMap[have.PackageName] == "" {
+ a.ToRemove = append(a.ToRemove, have)
+ }
+ }
+
+ if !a.Empty() {
+ aMap[subdir] = &a
+ }
+ return true
+ })
+
+ if len(aMap) == 0 {
+ return nil
+ }
return
}
« no previous file with comments | « no previous file | cipd/client/cipd/client_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698