Index: go/src/infra/tools/cipd/local/deployer.go |
diff --git a/go/src/infra/tools/cipd/local/deployer.go b/go/src/infra/tools/cipd/local/deployer.go |
index b2bcb7c7f320cf40dac56184a8f3296de5eff6b8..457e17bb5b9c6c0b7dafd2a1214f761f3304883d 100644 |
--- a/go/src/infra/tools/cipd/local/deployer.go |
+++ b/go/src/infra/tools/cipd/local/deployer.go |
@@ -14,7 +14,6 @@ import ( |
"sort" |
"strings" |
"sync" |
- "time" |
"github.com/luci/luci-go/common/logging" |
@@ -82,7 +81,7 @@ func NewDeployer(root string, logger logging.Logger) Deployer { |
if logger == nil { |
logger = logging.Null() |
} |
- return &deployerImpl{root, logger} |
+ return &deployerImpl{NewFileSystem(root, logger), logger} |
} |
//////////////////////////////////////////////////////////////////////////////// |
@@ -107,19 +106,19 @@ const currentSymlink = "_current" |
// deployerImpl implements Deployer interface. |
type deployerImpl struct { |
- root string |
+ fs FileSystem |
logger logging.Logger |
} |
func (d *deployerImpl) DeployInstance(inst PackageInstance) (common.Pin, error) { |
pin := inst.Pin() |
- d.logger.Infof("Deploying %s into %s", pin, d.root) |
+ d.logger.Infof("Deploying %s into %s", pin, d.fs.Root()) |
// Be paranoid. |
if err := common.ValidatePin(pin); err != nil { |
return common.Pin{}, err |
} |
- if err := d.ensureRootExists(); err != nil { |
+ if _, err := d.fs.EnsureDirectory(d.fs.Root()); err != nil { |
return common.Pin{}, err |
} |
@@ -138,9 +137,9 @@ func (d *deployerImpl) DeployInstance(inst PackageInstance) (common.Pin, error) |
// Switch '_current' symlink to point to a new package instance. It is a |
// point of no return. The function must not fail going forward. |
mainSymlinkPath := d.packagePath(pin.PackageName, currentSymlink) |
- err = ensureSymlink(mainSymlinkPath, pin.InstanceID) |
+ err = d.fs.EnsureSymlink(mainSymlinkPath, pin.InstanceID) |
if err != nil { |
- ensureDirectoryGone(destPath, d.logger) |
+ d.fs.EnsureDirectoryGone(destPath) |
return common.Pin{}, err |
} |
@@ -151,7 +150,7 @@ func (d *deployerImpl) DeployInstance(inst PackageInstance) (common.Pin, error) |
wg.Add(1) |
go func() { |
defer wg.Done() |
- ensureDirectoryGone(d.packagePath(pin.PackageName, prevID), d.logger) |
+ d.fs.EnsureDirectoryGone(d.packagePath(pin.PackageName, prevID)) |
}() |
} |
@@ -165,7 +164,7 @@ func (d *deployerImpl) DeployInstance(inst PackageInstance) (common.Pin, error) |
// Delete symlinks to files no longer needed i.e. set(old) - set(new). |
for relPath := range oldFiles.diff(newFiles) { |
- ensureFileGone(filepath.Join(d.root, relPath), d.logger) |
+ d.fs.EnsureFileGone(filepath.Join(d.fs.Root(), relPath)) |
} |
// Verify it's all right, read the manifest. |
@@ -191,7 +190,7 @@ func (d *deployerImpl) CheckDeployed(pkg string) (common.Pin, error) { |
func (d *deployerImpl) FindDeployed() (out []common.Pin, err error) { |
// Directories with packages are direct children of .cipd/pkgs/. |
- pkgs := filepath.Join(d.root, filepath.FromSlash(packagesDir)) |
+ pkgs := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir)) |
infos, err := ioutil.ReadDir(pkgs) |
if err != nil { |
if os.IsNotExist(err) { |
@@ -229,7 +228,7 @@ func (d *deployerImpl) FindDeployed() (out []common.Pin, err error) { |
} |
func (d *deployerImpl) RemoveDeployed(packageName string) error { |
- d.logger.Infof("Removing %s from %s", packageName, d.root) |
+ d.logger.Infof("Removing %s from %s", packageName, d.fs.Root()) |
// Be paranoid. |
err := common.ValidatePackageName(packageName) |
@@ -244,38 +243,25 @@ func (d *deployerImpl) RemoveDeployed(packageName string) error { |
// If was installed, removed symlinks pointing to the package files. |
if instanceID != "" { |
for relPath := range files { |
- ensureFileGone(filepath.Join(d.root, relPath), d.logger) |
+ d.fs.EnsureFileGone(filepath.Join(d.fs.Root(), relPath)) |
} |
} |
// Ensure all garbage is gone even if instanceID == "" was returned. |
- return ensureDirectoryGone(d.packagePath(packageName), d.logger) |
+ return d.fs.EnsureDirectoryGone(d.packagePath(packageName)) |
} |
func (d *deployerImpl) TempFile(prefix string) (*os.File, error) { |
- if err := d.ensureRootExists(); err != nil { |
- return nil, err |
- } |
- tempPath := filepath.Join(d.root, siteServiceDir, "tmp") |
- err := os.MkdirAll(tempPath, 0777) |
+ dir, err := d.fs.EnsureDirectory(filepath.Join(d.fs.Root(), siteServiceDir, "tmp")) |
if err != nil { |
return nil, err |
} |
- return ioutil.TempFile(tempPath, prefix) |
+ return ioutil.TempFile(dir, prefix) |
} |
//////////////////////////////////////////////////////////////////////////////// |
// Utility methods. |
-// ensureRootExists makes site root directory if it is missing. |
-func (d *deployerImpl) ensureRootExists() error { |
- err := os.MkdirAll(d.root, 0777) |
- if err == nil || os.IsExist(err) { |
- return nil |
- } |
- return err |
-} |
- |
// findDeployedInstance returns instanceID of a currently deployed package |
// instance and finds all files in it (adding them to 'files' set). Returns "" |
// if nothing is deployed. File paths in 'files' are relative to package root. |
@@ -296,14 +282,14 @@ func (d *deployerImpl) deployInstance(inst PackageInstance, files stringSet) (st |
// how to build full paths and how to atomically extract a package. No need |
// to delete garbage if it fails. |
destPath := d.packagePath(inst.Pin().PackageName, inst.Pin().InstanceID) |
- err := ExtractInstance(inst, NewFileSystemDestination(destPath)) |
+ err := ExtractInstance(inst, NewFileSystemDestination(destPath, d.fs)) |
if err != nil { |
return "", err |
} |
// Enumerate files inside. Nuke it and fail if it's unreadable. |
err = scanPackageDir(d.packagePath(inst.Pin().PackageName, inst.Pin().InstanceID), files) |
if err != nil { |
- ensureDirectoryGone(destPath, d.logger) |
+ d.fs.EnsureDirectoryGone(destPath) |
return "", err |
} |
return destPath, err |
@@ -315,7 +301,7 @@ func (d *deployerImpl) deployInstance(inst PackageInstance, files stringSet) (st |
func (d *deployerImpl) linkFilesToRoot(packageRoot string, files stringSet) { |
for relPath := range files { |
// E.g <root>/bin/tool. |
- symlinkAbs := filepath.Join(d.root, relPath) |
+ symlinkAbs := filepath.Join(d.fs.Root(), relPath) |
// E.g. <root>/.cipd/pkgs/name/_current/bin/tool. |
targetAbs := filepath.Join(packageRoot, relPath) |
// E.g. ../.cipd/pkgs/name/_current/bin/tool. |
@@ -324,7 +310,7 @@ func (d *deployerImpl) linkFilesToRoot(packageRoot string, files stringSet) { |
d.logger.Warningf("Can't get relative path from %s to %s", filepath.Dir(symlinkAbs), targetAbs) |
continue |
} |
- err = ensureSymlink(symlinkAbs, targetRel) |
+ err = d.fs.EnsureSymlink(symlinkAbs, targetRel) |
if err != nil { |
d.logger.Warningf("Failed to create symlink for %s", relPath) |
continue |
@@ -334,7 +320,7 @@ func (d *deployerImpl) linkFilesToRoot(packageRoot string, files stringSet) { |
// packagePath joins paths together to return absolute path to .cipd/pkgs sub path. |
func (d *deployerImpl) packagePath(pkg string, rest ...string) string { |
- root := filepath.Join(d.root, filepath.FromSlash(packagesDir), packageNameDigest(pkg)) |
+ root := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir), packageNameDigest(pkg)) |
result := filepath.Join(append([]string{root}, rest...)...) |
// Be paranoid and check that everything is inside .cipd directory. |
@@ -411,38 +397,6 @@ func readPackageState(packageDir string) (common.Pin, error) { |
}, nil |
} |
-// ensureSymlink atomically creates a symlink pointing to a target. It will |
-// create full directory path if necessary. |
-func ensureSymlink(symlink string, target string) error { |
- // Already set? |
- existing, err := os.Readlink(symlink) |
- if err != nil && existing == target { |
- return nil |
- } |
- |
- // Make sure path exists. |
- err = os.MkdirAll(filepath.Dir(symlink), 0777) |
- if err != nil { |
- return err |
- } |
- |
- // Create a new symlink file, can't modify existing one. |
- temp := fmt.Sprintf("%s_%v", symlink, time.Now().UnixNano()) |
- err = os.Symlink(target, temp) |
- if err != nil { |
- return err |
- } |
- |
- // Atomically replace current symlink with a new one. |
- err = os.Rename(temp, symlink) |
- if err != nil { |
- os.Remove(temp) |
- return err |
- } |
- |
- return nil |
-} |
- |
// scanPackageDir finds a set of regular files (and symlinks) in a package |
// instance directory. Adds paths relative to 'dir' to 'out'. Skips package |
// service directories (.cipdpkg and .cipd) since they contain package deployer |
@@ -466,42 +420,6 @@ func scanPackageDir(dir string, out stringSet) error { |
}) |
} |
-// ensureDirectoryGone removes the directory as instantly as possible by |
-// renaming it first and only then recursively deleting. |
-func ensureDirectoryGone(path string, logger logging.Logger) error { |
- temp := fmt.Sprintf("%s_%v", path, time.Now().UnixNano()) |
- err := os.Rename(path, temp) |
- if err != nil { |
- if !os.IsNotExist(err) { |
- if logger != nil { |
- logger.Warningf("Failed to rename directory %s: %v", path, err) |
- } |
- return err |
- } |
- return nil |
- } |
- err = os.RemoveAll(temp) |
- if err != nil { |
- if logger != nil { |
- logger.Warningf("Failed to remove directory %s: %v", temp, err) |
- } |
- return err |
- } |
- return nil |
-} |
- |
-// ensureFileGone removes file, logging the errors (if any). |
-func ensureFileGone(path string, logger logging.Logger) error { |
- err := os.Remove(path) |
- if err != nil && !os.IsNotExist(err) { |
- if logger != nil { |
- logger.Warningf("Failed to remove %s", path) |
- } |
- return err |
- } |
- return nil |
-} |
- |
//////////////////////////////////////////////////////////////////////////////// |
// Simple stringSet implementation for keeping a set of filenames. |