Index: go/src/infra/tools/cipd/ensure.go |
diff --git a/go/src/infra/tools/cipd/ensure.go b/go/src/infra/tools/cipd/ensure.go |
deleted file mode 100644 |
index 549bddd050ace9125eb393c2ec4e3927a550b177..0000000000000000000000000000000000000000 |
--- a/go/src/infra/tools/cipd/ensure.go |
+++ /dev/null |
@@ -1,231 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-package cipd |
- |
-import ( |
- "bufio" |
- "fmt" |
- "io" |
- "net/http" |
- "os" |
- "path/filepath" |
- "strings" |
- |
- "infra/libs/logging" |
-) |
- |
-// ParseDesiredState parses text file that describes what should be installed |
-// by EnsurePackages function. It is a text file where each line has a form: |
-// <package name> <desired instance ID> |
-// Whitespaces are ignored. Lines that start with '#' are ignored. |
-func ParseDesiredState(r io.Reader) ([]PackageState, error) { |
- lineNo := 0 |
- makeError := func(msg string) error { |
- return fmt.Errorf("Failed to parse desired state (line %d): %s", lineNo, msg) |
- } |
- |
- out := []PackageState{} |
- 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> <instance id>". |
- if len(tokens) != 2 { |
- return nil, makeError("expecting '<package name> <instance id>' line") |
- } |
- err := ValidatePackageName(tokens[0]) |
- if err != nil { |
- return nil, makeError(err.Error()) |
- } |
- err = ValidateInstanceID(tokens[1]) |
- if err != nil { |
- return nil, makeError(err.Error()) |
- } |
- |
- // Good enough. |
- out = append(out, PackageState{ |
- PackageName: tokens[0], |
- InstanceID: tokens[1], |
- }) |
- } |
- |
- return out, nil |
-} |
- |
-// EnsurePackagesOptions contains parameters for EnsurePackages calls. |
-type EnsurePackagesOptions struct { |
- // ServiceURL is root URL of the backend service, or "" to use default service. |
- ServiceURL string |
- // ClientFactory knows how to make authenticated http.Client when it is needed. Called lazily. |
- ClientFactory func() (*http.Client, error) |
- // Log is a logger to use for logs, default is logging.DefaultLogger. |
- Log logging.Logger |
- |
- // Root is a site root directory to modify. Will be created if missing. |
- Root string |
- // Packages describes the desired state of the site root directory. |
- Packages []PackageState |
-} |
- |
-// EnsurePackages is high level interface for installing, removing and updating |
-// of packages inside some installation site root. Given a description of |
-// what packages (and versions) should be installed it will do all necessary |
-// actions to bring the state of the site root to desired one. |
-func EnsurePackages(opts EnsurePackagesOptions) error { |
- // Make sure a package is specified only once. |
- seen := make(map[string]bool, len(opts.Packages)) |
- for _, p := range opts.Packages { |
- if seen[p.PackageName] { |
- return fmt.Errorf("Package %s is specified twice", p.PackageName) |
- } |
- seen[p.PackageName] = true |
- } |
- |
- // Fill in default options. |
- if opts.ServiceURL == "" { |
- opts.ServiceURL = DefaultServiceURL() |
- } |
- if opts.Log == nil { |
- opts.Log = logging.DefaultLogger |
- } |
- log := opts.Log |
- |
- // Ensure site root is a directory (or missing). |
- root, err := filepath.Abs(filepath.Clean(opts.Root)) |
- if err != nil { |
- return err |
- } |
- stat, err := os.Stat(root) |
- if err == nil && !stat.IsDir() { |
- return fmt.Errorf("Path %s is not a directory", opts.Root) |
- } |
- if err != nil && !os.IsNotExist(err) { |
- return err |
- } |
- rootExists := (err == nil) |
- |
- // Enumerate existing packages (only if root already exists). |
- existing := []PackageState{} |
- if rootExists { |
- existing, err = FindDeployed(root) |
- if err != nil { |
- log.Errorf("Failed to enumerate installed packages: %s", err) |
- return err |
- } |
- } |
- |
- // Figure out what needs to be updated and deleted, log it. |
- toDeploy, toDelete := buildActionPlan(opts.Packages, existing) |
- if len(toDeploy) == 0 && len(toDelete) == 0 { |
- log.Infof("Everything is up-to-date.") |
- return nil |
- } |
- if len(toDeploy) != 0 { |
- log.Infof("Packages to be installed:") |
- for _, state := range toDeploy { |
- log.Infof(" %s:%s", state.PackageName, state.InstanceID) |
- } |
- } |
- if len(toDelete) != 0 { |
- log.Infof("Packages to be removed:") |
- for _, state := range toDelete { |
- log.Infof(" %s", state.PackageName) |
- } |
- } |
- |
- // Create the site root directory before installing anything there. |
- if len(toDeploy) != 0 && !rootExists { |
- err = os.MkdirAll(root, 0777) |
- if err != nil { |
- return err |
- } |
- } |
- |
- // Updating packages requires interaction with the server, create the client. |
- client := http.DefaultClient |
- if len(toDeploy) != 0 && opts.ClientFactory != nil { |
- client, err = opts.ClientFactory() |
- if err != nil { |
- return err |
- } |
- } |
- |
- // Remove all unneeded stuff. |
- errors := []error{} |
- for _, state := range toDelete { |
- err = RemoveDeployed(root, state.PackageName) |
- if err != nil { |
- log.Errorf("Failed to remove %s - %s", state.PackageName, err) |
- errors = append(errors, err) |
- } |
- } |
- |
- // Install all new stuff. |
- for _, state := range toDeploy { |
- err = FetchAndDeployInstance(root, FetchInstanceOptions{ |
- ServiceURL: opts.ServiceURL, |
- Client: client, |
- Log: opts.Log, |
- PackageName: state.PackageName, |
- InstanceID: state.InstanceID, |
- }) |
- if err != nil { |
- log.Errorf("Failed to install %s:%s - %s", state.PackageName, state.InstanceID, err) |
- errors = append(errors, err) |
- } |
- } |
- |
- if len(errors) == 0 { |
- log.Infof("All changes applied.") |
- return nil |
- } |
- return fmt.Errorf("Some actions failed: %v", errors) |
-} |
- |
-func buildActionPlan(desired []PackageState, existing []PackageState) (toDeploy []PackageState, toDelete []PackageState) { |
- // Figure out what needs to be installed or updated. |
- for _, d := range desired { |
- alreadyGood := false |
- for _, e := range existing { |
- if e.PackageName == d.PackageName { |
- alreadyGood = e.InstanceID == d.InstanceID |
- break |
- } |
- } |
- if !alreadyGood { |
- toDeploy = append(toDeploy, d) |
- } |
- } |
- |
- // Figure out what needs to be removed. |
- for _, e := range existing { |
- keep := false |
- for _, d := range desired { |
- if e.PackageName == d.PackageName { |
- keep = true |
- break |
- } |
- } |
- if !keep { |
- toDelete = append(toDelete, e) |
- } |
- } |
- |
- return |
-} |