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

Side by Side Diff: go/src/infra/tools/cipd/local/deployer.go

Issue 1129043003: cipd: Refactor client to make it more readable. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 7 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package cipd 5 package local
6 6
7 import ( 7 import (
8 "crypto/sha1" 8 "crypto/sha1"
9 "encoding/base64" 9 "encoding/base64"
10 "fmt" 10 "fmt"
11 "io/ioutil" 11 "io/ioutil"
12 "os" 12 "os"
13 "path/filepath" 13 "path/filepath"
14 "sort" 14 "sort"
15 "strings" 15 "strings"
16 "sync" 16 "sync"
17 "time" 17 "time"
18
19 "infra/libs/logging"
20 "infra/tools/cipd/common"
18 ) 21 )
19 22
20 // TODO(vadimsh): Make it work on Windows, verify it works on Mac. 23 // TODO(vadimsh): Make it work on Windows.
21 24
22 // TODO(vadimsh): How to handle path conflicts between two packages? Currently 25 // TODO(vadimsh): How to handle path conflicts between two packages? Currently
23 // the last one installed wins. 26 // the last one installed wins.
24 27
25 // File system layout of a site directory <root>: 28 // File system layout of a site directory <root>:
26 // <root>/.cipd/pkgs/ 29 // <root>/.cipd/pkgs/
27 // <package name digest>/ 30 // <package name digest>/
28 // _current -> symlink to fea3ab83440e9dfb813785e16d4101f331ed44f4 31 // _current -> symlink to fea3ab83440e9dfb813785e16d4101f331ed44f4
29 // fea3ab83440e9dfb813785e16d4101f331ed44f4/ 32 // fea3ab83440e9dfb813785e16d4101f331ed44f4/
30 // bin/ 33 // bin/
31 // tool 34 // tool
32 // ... 35 // ...
33 // ... 36 // ...
34 // bin/ 37 // bin/
35 // tool -> symlink to ../.cipd/pkgs/<package name digest>/_current/bin/tool 38 // tool -> symlink to ../.cipd/pkgs/<package name digest>/_current/bin/tool
36 // ... 39 // ...
37 // 40 //
38 // Where <package name digest> is derived from a package name. It doesn't have 41 // Where <package name digest> is derived from a package name. It doesn't have
39 // to be reversible though, since the package name is still stored in the 42 // to be reversible though, since the package name is still stored in the
40 // installed package manifest and can be read from there. 43 // installed package manifest and can be read from there.
41 // 44 //
42 // Some efforts are made to make sure that during the deployment a window of 45 // Some efforts are made to make sure that during the deployment a window of
43 // inconsistency in the file system is as small as possible. 46 // inconsistency in the file system is as small as possible.
44 47
45 // Subdirectory of site root to extract packages to. 48 // packagesDir is a subdirectory of site root to extract packages to.
46 const packagesDir = siteServiceDir + "/pkgs" 49 const packagesDir = SiteServiceDir + "/pkgs"
47 50
48 // Name of a symlink that points to latest deployed version. 51 // currentSymlink is a name of a symlink that points to latest deployed version.
49 const currentSymlink = "_current" 52 const currentSymlink = "_current"
50 53
51 // PackageState contains information about single deployed package. 54 // log is logger to use for logs produced by this file.
52 type PackageState struct { 55 // TODO(vadimsh): Propagate as parameter.
53 » // PackageName identifies the package. 56 var log = logging.DefaultLogger
54 » PackageName string
55 » // InstanceID is ID of the installed package instance (SHA1 of package c ontents).
56 » InstanceID string
57 }
58 57
59 // DeployInstance installs a specific instance of a package (identified by 58 // DeployInstance installs a specific instance of a package into a site root
60 // InstanceID()) into a site root directory. It unpacks the package into 59 // directory. It unpacks the package into <root>/.cipd/pkgs/*, and rearranges
61 // <root>/.cipd/pkgs/*, and rearranges symlinks to point to unpacked files. 60 // symlinks to point to unpacked files. It tries to make it as "atomic" as
62 // It tries to make it as "atomic" as possibly. 61 // possible. Returns information about the deployed instance.
63 func DeployInstance(root string, inst PackageInstance) (state PackageState, err error) { 62 func DeployInstance(root string, inst PackageInstance) (common.Pin, error) {
64 » root, err = filepath.Abs(filepath.Clean(root)) 63 » root, err := filepath.Abs(filepath.Clean(root))
65 if err != nil { 64 if err != nil {
66 » » return 65 » » return common.Pin{}, err
67 } 66 }
68 » log.Infof("Deploying %s:%s into %s", inst.PackageName(), inst.InstanceID (), root) 67 » pin := inst.Pin()
68 » log.Infof("Deploying %s into %s", pin, root)
69 69
70 // Be paranoid. 70 // Be paranoid.
71 » err = ValidatePackageName(inst.PackageName()) 71 » err = common.ValidatePin(pin)
72 if err != nil { 72 if err != nil {
73 » » return 73 » » return common.Pin{}, err
74 » }
75 » err = ValidateInstanceID(inst.InstanceID())
76 » if err != nil {
77 » » return
78 } 74 }
79 75
80 // Remember currently deployed version (to remove it later). Do not frea k out 76 // Remember currently deployed version (to remove it later). Do not frea k out
81 // if it's not there (prevID is "" in that case). 77 // if it's not there (prevID is "" in that case).
82 oldFiles := makeStringSet() 78 oldFiles := makeStringSet()
83 » prevID := findDeployedInstance(root, inst.PackageName(), oldFiles) 79 » prevID := findDeployedInstance(root, pin.PackageName, oldFiles)
84 80
85 // Extract new version to a final destination. 81 // Extract new version to a final destination.
86 newFiles := makeStringSet() 82 newFiles := makeStringSet()
87 destPath, err := deployInstance(root, inst, newFiles) 83 destPath, err := deployInstance(root, inst, newFiles)
88 if err != nil { 84 if err != nil {
89 » » return 85 » » return common.Pin{}, err
90 } 86 }
91 87
92 // Switch '_current' symlink to point to a new package instance. It is a 88 // Switch '_current' symlink to point to a new package instance. It is a
93 // point of no return. The function must not fail going forward. 89 // point of no return. The function must not fail going forward.
94 » mainSymlinkPath := packagePath(root, inst.PackageName(), currentSymlink) 90 » mainSymlinkPath := packagePath(root, pin.PackageName, currentSymlink)
95 » err = ensureSymlink(mainSymlinkPath, inst.InstanceID()) 91 » err = ensureSymlink(mainSymlinkPath, pin.InstanceID)
96 if err != nil { 92 if err != nil {
97 ensureDirectoryGone(destPath) 93 ensureDirectoryGone(destPath)
98 » » return 94 » » return common.Pin{}, err
99 } 95 }
100 96
101 // Asynchronously remove previous version (best effort). 97 // Asynchronously remove previous version (best effort).
102 wg := sync.WaitGroup{} 98 wg := sync.WaitGroup{}
103 defer wg.Wait() 99 defer wg.Wait()
104 » if prevID != "" && prevID != inst.InstanceID() { 100 » if prevID != "" && prevID != pin.InstanceID {
105 wg.Add(1) 101 wg.Add(1)
106 go func() { 102 go func() {
107 defer wg.Done() 103 defer wg.Done()
108 » » » ensureDirectoryGone(packagePath(root, inst.PackageName() , prevID)) 104 » » » ensureDirectoryGone(packagePath(root, pin.PackageName, p revID))
109 }() 105 }()
110 } 106 }
111 107
112 » log.Infof("Adjusting symlinks for %s", inst.PackageName()) 108 » log.Infof("Adjusting symlinks for %s", pin.PackageName)
113 109
114 // Make symlinks in the site directory for all new files. Reference a pa ckage 110 // Make symlinks in the site directory for all new files. Reference a pa ckage
115 // root via '_current' symlink (instead of direct destPath), to make 111 // root via '_current' symlink (instead of direct destPath), to make
116 // subsequent updates 'more atomic' (since they'll need to switch only 112 // subsequent updates 'more atomic' (since they'll need to switch only
117 // '_current' symlink to update _all_ files in the site root at once). 113 // '_current' symlink to update _all_ files in the site root at once).
118 linkFilesToRoot(root, mainSymlinkPath, newFiles) 114 linkFilesToRoot(root, mainSymlinkPath, newFiles)
119 115
120 // Delete symlinks to files no longer needed i.e. set(old) - set(new). 116 // Delete symlinks to files no longer needed i.e. set(old) - set(new).
121 for relPath := range oldFiles.diff(newFiles) { 117 for relPath := range oldFiles.diff(newFiles) {
122 ensureFileGone(filepath.Join(root, relPath)) 118 ensureFileGone(filepath.Join(root, relPath))
123 } 119 }
124 120
125 // Verify it's all right, read the manifest. 121 // Verify it's all right, read the manifest.
126 » state, err = CheckDeployed(root, inst.PackageName()) 122 » newPin, err := CheckDeployed(root, pin.PackageName)
127 » if err == nil && state.InstanceID != inst.InstanceID() { 123 » if err == nil && newPin.InstanceID != pin.InstanceID {
128 » » err = fmt.Errorf("Other package instance (%s) was deployed concu rrently", state.InstanceID) 124 » » err = fmt.Errorf("Other instance (%s) was deployed concurrently" , newPin.InstanceID)
129 } 125 }
130 if err == nil { 126 if err == nil {
131 » » log.Infof("Successfully deployed %s:%s", inst.PackageName(), ins t.InstanceID()) 127 » » log.Infof("Successfully deployed %s", pin)
132 } else { 128 } else {
133 » » log.Errorf("Failed to deploy %s:%s: %s", inst.PackageName(), ins t.InstanceID(), err.Error()) 129 » » log.Errorf("Failed to deploy %s: %s", pin, err)
134 } 130 }
135 » return 131 » return newPin, err
136 } 132 }
137 133
138 // CheckDeployed checks whether a given package is deployed and returns 134 // CheckDeployed checks whether a given package is deployed and returns
139 // information about it if it is. 135 // information about it if it is.
140 func CheckDeployed(root string, pkg string) (state PackageState, err error) { 136 func CheckDeployed(root string, pkg string) (common.Pin, error) {
141 » state, err = readPackageState(packagePath(root, pkg)) 137 » pin, err := readPackageState(packagePath(root, pkg))
142 » if err != nil { 138 » if err == nil && pin.PackageName != pkg {
143 » » return
144 » }
145 » if state.PackageName != pkg {
146 err = fmt.Errorf("Package path and package name in the manifest do not match") 139 err = fmt.Errorf("Package path and package name in the manifest do not match")
147 } 140 }
148 » return 141 » return pin, err
149 } 142 }
150 143
151 // FindDeployed returns a list of packages deployed to a site root. 144 // FindDeployed returns a list of packages deployed to a site root.
152 func FindDeployed(root string) (out []PackageState, err error) { 145 func FindDeployed(root string) (out []common.Pin, err error) {
153 root, err = filepath.Abs(filepath.Clean(root)) 146 root, err = filepath.Abs(filepath.Clean(root))
154 if err != nil { 147 if err != nil {
155 return 148 return
156 } 149 }
157 150
158 // Directories with packages are direct children of .cipd/pkgs/. 151 // Directories with packages are direct children of .cipd/pkgs/.
159 pkgs := filepath.Join(root, filepath.FromSlash(packagesDir)) 152 pkgs := filepath.Join(root, filepath.FromSlash(packagesDir))
160 infos, err := ioutil.ReadDir(pkgs) 153 infos, err := ioutil.ReadDir(pkgs)
161 if err != nil { 154 if err != nil {
162 if os.IsNotExist(err) { 155 if os.IsNotExist(err) {
163 err = nil 156 err = nil
164 return 157 return
165 } 158 }
166 return 159 return
167 } 160 }
168 161
169 // Read the package name from the package manifest. Skip broken stuff. 162 // Read the package name from the package manifest. Skip broken stuff.
170 » found := map[string]PackageState{} 163 » found := map[string]common.Pin{}
171 keys := []string{} 164 keys := []string{}
172 for _, info := range infos { 165 for _, info := range infos {
173 // Attempt to read the manifest. If it is there -> valid package is found. 166 // Attempt to read the manifest. If it is there -> valid package is found.
174 if info.IsDir() { 167 if info.IsDir() {
175 » » » state, err := readPackageState(filepath.Join(pkgs, info. Name())) 168 » » » pin, err := readPackageState(filepath.Join(pkgs, info.Na me()))
176 if err == nil { 169 if err == nil {
177 // Ignore duplicate entries, they can appear if someone messes with 170 // Ignore duplicate entries, they can appear if someone messes with
178 // pkgs/* structure manually. 171 // pkgs/* structure manually.
179 » » » » if _, ok := found[state.PackageName]; !ok { 172 » » » » if _, ok := found[pin.PackageName]; !ok {
180 » » » » » keys = append(keys, state.PackageName) 173 » » » » » keys = append(keys, pin.PackageName)
181 » » » » » found[state.PackageName] = state 174 » » » » » found[pin.PackageName] = pin
182 } 175 }
183 } 176 }
184 } 177 }
185 } 178 }
186 179
187 // Sort by package name. 180 // Sort by package name.
188 sort.Strings(keys) 181 sort.Strings(keys)
189 » out = make([]PackageState, len(found)) 182 » out = make([]common.Pin, len(found))
190 for i, k := range keys { 183 for i, k := range keys {
191 out[i] = found[k] 184 out[i] = found[k]
192 } 185 }
193 return 186 return
194 } 187 }
195 188
196 // RemoveDeployed deletes a package given its name. 189 // RemoveDeployed deletes a package given its name.
197 func RemoveDeployed(root string, packageName string) error { 190 func RemoveDeployed(root string, packageName string) error {
198 root, err := filepath.Abs(filepath.Clean(root)) 191 root, err := filepath.Abs(filepath.Clean(root))
199 if err != nil { 192 if err != nil {
200 return err 193 return err
201 } 194 }
202 log.Infof("Removing %s from %s", packageName, root) 195 log.Infof("Removing %s from %s", packageName, root)
203 196
204 // Be paranoid. 197 // Be paranoid.
205 » err = ValidatePackageName(packageName) 198 » err = common.ValidatePackageName(packageName)
206 if err != nil { 199 if err != nil {
207 return err 200 return err
208 } 201 }
209 202
210 // Grab list of files in currently deployed package to unlink them from root. 203 // Grab list of files in currently deployed package to unlink them from root.
211 files := makeStringSet() 204 files := makeStringSet()
212 instanceID := findDeployedInstance(root, packageName, files) 205 instanceID := findDeployedInstance(root, packageName, files)
213 206
214 // If was installed, removed symlinks pointing to the package files. 207 // If was installed, removed symlinks pointing to the package files.
215 if instanceID != "" { 208 if instanceID != "" {
(...skipping 21 matching lines...) Expand all
237 return state.InstanceID 230 return state.InstanceID
238 } 231 }
239 232
240 // deployInstance atomically extracts a package instance to its final 233 // deployInstance atomically extracts a package instance to its final
241 // destination and returns a path to it. It writes a list of extracted files 234 // destination and returns a path to it. It writes a list of extracted files
242 // to 'files'. File paths in 'files' are relative to package root. 235 // to 'files'. File paths in 'files' are relative to package root.
243 func deployInstance(root string, inst PackageInstance, files stringSet) (string, error) { 236 func deployInstance(root string, inst PackageInstance, files stringSet) (string, error) {
244 // Extract new version to a final destination. ExtractPackageInstance kn ows 237 // Extract new version to a final destination. ExtractPackageInstance kn ows
245 // how to build full paths and how to atomically extract a package. No n eed 238 // how to build full paths and how to atomically extract a package. No n eed
246 // to delete garbage if it fails. 239 // to delete garbage if it fails.
247 » destPath := packagePath(root, inst.PackageName(), inst.InstanceID()) 240 » destPath := packagePath(root, inst.Pin().PackageName, inst.Pin().Instanc eID)
248 err := ExtractInstance(inst, NewFileSystemDestination(destPath)) 241 err := ExtractInstance(inst, NewFileSystemDestination(destPath))
249 if err != nil { 242 if err != nil {
250 return "", err 243 return "", err
251 } 244 }
252 // Enumerate files inside. Nuke it and fail if it's unreadable. 245 // Enumerate files inside. Nuke it and fail if it's unreadable.
253 » err = scanPackageDir(packagePath(root, inst.PackageName(), inst.Instance ID()), files) 246 » err = scanPackageDir(packagePath(root, inst.Pin().PackageName, inst.Pin( ).InstanceID), files)
254 if err != nil { 247 if err != nil {
255 ensureDirectoryGone(destPath) 248 ensureDirectoryGone(destPath)
256 return "", err 249 return "", err
257 } 250 }
258 return destPath, err 251 return destPath, err
259 } 252 }
260 253
261 // linkFilesToRoot makes symlinks in root that point to files in packageRoot. 254 // linkFilesToRoot makes symlinks in root that point to files in packageRoot.
262 // All targets are specified by 'files' as paths relative to packageRoot. This 255 // All targets are specified by 'files' as paths relative to packageRoot. This
263 // function is best effort and thus doesn't return errors. 256 // function is best effort and thus doesn't return errors.
264 func linkFilesToRoot(root string, packageRoot string, files stringSet) { 257 func linkFilesToRoot(root string, packageRoot string, files stringSet) {
265 for relPath := range files { 258 for relPath := range files {
266 // E.g <root>/bin/tool. 259 // E.g <root>/bin/tool.
267 symlinkAbs := filepath.Join(root, relPath) 260 symlinkAbs := filepath.Join(root, relPath)
268 // E.g. <root>/.cipd/pkgs/name/_current/bin/tool. 261 // E.g. <root>/.cipd/pkgs/name/_current/bin/tool.
269 targetAbs := filepath.Join(packageRoot, relPath) 262 targetAbs := filepath.Join(packageRoot, relPath)
270 // E.g. ../.cipd/pkgs/name/_current/bin/tool. 263 // E.g. ../.cipd/pkgs/name/_current/bin/tool.
271 targetRel, err := filepath.Rel(filepath.Dir(symlinkAbs), targetA bs) 264 targetRel, err := filepath.Rel(filepath.Dir(symlinkAbs), targetA bs)
272 if err != nil { 265 if err != nil {
273 » » » log.Warnf("Can't get relative path from %s to %s", filep ath.Dir(symlinkAbs), targetAbs) 266 » » » log.Warningf("Can't get relative path from %s to %s", fi lepath.Dir(symlinkAbs), targetAbs)
274 continue 267 continue
275 } 268 }
276 err = ensureSymlink(symlinkAbs, targetRel) 269 err = ensureSymlink(symlinkAbs, targetRel)
277 if err != nil { 270 if err != nil {
278 » » » log.Warnf("Failed to create symlink for %s", relPath) 271 » » » log.Warningf("Failed to create symlink for %s", relPath)
279 continue 272 continue
280 } 273 }
281 } 274 }
282 } 275 }
283 276
284 // packagePath joins paths together to return absolute path to .cipd/pkgs sub pa th. 277 // packagePath joins paths together to return absolute path to .cipd/pkgs sub pa th.
285 func packagePath(root string, pkg string, rest ...string) string { 278 func packagePath(root string, pkg string, rest ...string) string {
286 root, err := filepath.Abs(filepath.Clean(root)) 279 root, err := filepath.Abs(filepath.Clean(root))
287 if err != nil { 280 if err != nil {
288 panic(fmt.Sprintf("Can't get absolute path of '%s'", root)) 281 panic(fmt.Sprintf("Can't get absolute path of '%s'", root))
(...skipping 11 matching lines...) Expand all
300 } 293 }
301 return result 294 return result
302 } 295 }
303 296
304 // packageNameDigest returns a filename to use for naming a package directory in 297 // packageNameDigest returns a filename to use for naming a package directory in
305 // the file system. Using package names as is can introduce problems on file 298 // the file system. Using package names as is can introduce problems on file
306 // systems with path length limits (on Windows in particular). Returns last two 299 // systems with path length limits (on Windows in particular). Returns last two
307 // components of the package name + stripped SHA1 of the whole package name. 300 // components of the package name + stripped SHA1 of the whole package name.
308 func packageNameDigest(pkg string) string { 301 func packageNameDigest(pkg string) string {
309 // Be paranoid. 302 // Be paranoid.
310 » err := ValidatePackageName(pkg) 303 » err := common.ValidatePackageName(pkg)
311 if err != nil { 304 if err != nil {
312 panic(err.Error()) 305 panic(err.Error())
313 } 306 }
314 307
315 // Grab stripped SHA1 of the full package name. 308 // Grab stripped SHA1 of the full package name.
316 h := sha1.New() 309 h := sha1.New()
317 h.Write([]byte(pkg)) 310 h.Write([]byte(pkg))
318 hash := base64.URLEncoding.EncodeToString(h.Sum(nil))[:10] 311 hash := base64.URLEncoding.EncodeToString(h.Sum(nil))[:10]
319 312
320 // Grab last <= 2 components of the package path. 313 // Grab last <= 2 components of the package path.
321 chunks := strings.Split(pkg, "/") 314 chunks := strings.Split(pkg, "/")
322 if len(chunks) > 2 { 315 if len(chunks) > 2 {
323 chunks = chunks[len(chunks)-2:] 316 chunks = chunks[len(chunks)-2:]
324 } 317 }
325 318
326 // Join together with '_' as separator. 319 // Join together with '_' as separator.
327 chunks = append(chunks, hash) 320 chunks = append(chunks, hash)
328 return strings.Join(chunks, "_") 321 return strings.Join(chunks, "_")
329 } 322 }
330 323
331 // readPackageState reads package manifest of a deployed package instance and 324 // readPackageState reads package manifest of a deployed package instance and
332 // returns corresponding PackageState object. 325 // returns corresponding Pin object.
333 func readPackageState(packageDir string) (state PackageState, err error) { 326 func readPackageState(packageDir string) (common.Pin, error) {
334 // Resolve _current symlink to a concrete instance ID. 327 // Resolve _current symlink to a concrete instance ID.
335 current, err := os.Readlink(filepath.Join(packageDir, currentSymlink)) 328 current, err := os.Readlink(filepath.Join(packageDir, currentSymlink))
336 if err != nil { 329 if err != nil {
337 » » return 330 » » return common.Pin{}, err
338 } 331 }
339 » err = ValidateInstanceID(current) 332 » err = common.ValidateInstanceID(current)
340 if err != nil { 333 if err != nil {
341 » » err = fmt.Errorf("Symlink target doesn't look like a valid insta nce id") 334 » » return common.Pin{}, fmt.Errorf("Symlink target doesn't look lik e a valid instance id")
342 » » return
343 } 335 }
344 // Read the manifest from the instance directory. 336 // Read the manifest from the instance directory.
345 manifestPath := filepath.Join(packageDir, current, filepath.FromSlash(ma nifestName)) 337 manifestPath := filepath.Join(packageDir, current, filepath.FromSlash(ma nifestName))
346 r, err := os.Open(manifestPath) 338 r, err := os.Open(manifestPath)
347 if err != nil { 339 if err != nil {
348 » » return 340 » » return common.Pin{}, err
349 } 341 }
350 defer r.Close() 342 defer r.Close()
351 manifest, err := readManifest(r) 343 manifest, err := readManifest(r)
352 if err != nil { 344 if err != nil {
353 » » return 345 » » return common.Pin{}, err
354 } 346 }
355 » state = PackageState{ 347 » return common.Pin{
356 PackageName: manifest.PackageName, 348 PackageName: manifest.PackageName,
357 InstanceID: current, 349 InstanceID: current,
358 » } 350 » }, nil
359 » return
360 } 351 }
361 352
362 // ensureSymlink atomically creates a symlink pointing to a target. It will 353 // ensureSymlink atomically creates a symlink pointing to a target. It will
363 // create full directory path if necessary. 354 // create full directory path if necessary.
364 func ensureSymlink(symlink string, target string) error { 355 func ensureSymlink(symlink string, target string) error {
365 // Already set? 356 // Already set?
366 existing, err := os.Readlink(symlink) 357 existing, err := os.Readlink(symlink)
367 if err != nil && existing == target { 358 if err != nil && existing == target {
368 return nil 359 return nil
369 } 360 }
(...skipping 28 matching lines...) Expand all
398 // to be deployed. 389 // to be deployed.
399 func scanPackageDir(root string, out stringSet) error { 390 func scanPackageDir(root string, out stringSet) error {
400 return filepath.Walk(root, func(path string, info os.FileInfo, err error ) error { 391 return filepath.Walk(root, func(path string, info os.FileInfo, err error ) error {
401 if err != nil { 392 if err != nil {
402 return err 393 return err
403 } 394 }
404 rel, err := filepath.Rel(root, path) 395 rel, err := filepath.Rel(root, path)
405 if err != nil { 396 if err != nil {
406 return err 397 return err
407 } 398 }
408 » » if rel == packageServiceDir || rel == siteServiceDir { 399 » » if rel == packageServiceDir || rel == SiteServiceDir {
409 return filepath.SkipDir 400 return filepath.SkipDir
410 } 401 }
411 if info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 { 402 if info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 {
412 out.add(rel) 403 out.add(rel)
413 } 404 }
414 return nil 405 return nil
415 }) 406 })
416 } 407 }
417 408
418 // ensureDirectoryGone removes the directory as instantly as possible by 409 // ensureDirectoryGone removes the directory as instantly as possible by
419 // renaming it first and only then recursively deleting. 410 // renaming it first and only then recursively deleting.
420 func ensureDirectoryGone(path string) error { 411 func ensureDirectoryGone(path string) error {
421 temp := fmt.Sprintf("%s_%v", path, time.Now().UnixNano()) 412 temp := fmt.Sprintf("%s_%v", path, time.Now().UnixNano())
422 err := os.Rename(path, temp) 413 err := os.Rename(path, temp)
423 if err != nil { 414 if err != nil {
424 if !os.IsNotExist(err) { 415 if !os.IsNotExist(err) {
425 » » » log.Warnf("Failed to rename directory %s: %v", path, err ) 416 » » » log.Warningf("Failed to rename directory %s: %v", path, err)
426 return err 417 return err
427 } 418 }
428 return nil 419 return nil
429 } 420 }
430 err = os.RemoveAll(temp) 421 err = os.RemoveAll(temp)
431 if err != nil { 422 if err != nil {
432 » » log.Warnf("Failed to remove directory %s: %v", temp, err) 423 » » log.Warningf("Failed to remove directory %s: %v", temp, err)
433 return err 424 return err
434 } 425 }
435 return nil 426 return nil
436 } 427 }
437 428
438 // ensureFileGone removes file, logging the errors (if any). 429 // ensureFileGone removes file, logging the errors (if any).
439 func ensureFileGone(path string) error { 430 func ensureFileGone(path string) error {
440 err := os.Remove(path) 431 err := os.Remove(path)
441 if err != nil && !os.IsNotExist(err) { 432 if err != nil && !os.IsNotExist(err) {
442 » » log.Warnf("Failed to remove %s", path) 433 » » log.Warningf("Failed to remove %s", path)
443 return err 434 return err
444 } 435 }
445 return nil 436 return nil
446 } 437 }
447 438
448 //////////////////////////////////////////////////////////////////////////////// 439 ////////////////////////////////////////////////////////////////////////////////
449 // Simple stringSet implementation for keeping a set of filenames. 440 // Simple stringSet implementation for keeping a set of filenames.
450 441
451 type stringSet map[string]struct{} 442 type stringSet map[string]struct{}
452 443
453 func makeStringSet() stringSet { 444 func makeStringSet() stringSet {
454 return make(stringSet) 445 return make(stringSet)
455 } 446 }
456 447
457 // add adds an element to the string set. 448 // add adds an element to the string set.
458 func (a stringSet) add(elem string) { 449 func (a stringSet) add(elem string) {
459 a[elem] = struct{}{} 450 a[elem] = struct{}{}
460 } 451 }
461 452
462 // diff returns set(a) - set(b). 453 // diff returns set(a) - set(b).
463 func (a stringSet) diff(b stringSet) stringSet { 454 func (a stringSet) diff(b stringSet) stringSet {
464 out := makeStringSet() 455 out := makeStringSet()
465 for elem := range a { 456 for elem := range a {
466 if _, ok := b[elem]; !ok { 457 if _, ok := b[elem]; !ok {
467 out.add(elem) 458 out.add(elem)
468 } 459 }
469 } 460 }
470 return out 461 return out
471 } 462 }
OLDNEW
« no previous file with comments | « go/src/infra/tools/cipd/local/builder_test.go ('k') | go/src/infra/tools/cipd/local/deployer_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698