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

Side by Side Diff: cipd/client/cipd/local/deployer.go

Issue 2657293005: [cipd] Add subdir support to deployer. (Closed)
Patch Set: Created 3 years, 10 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
« no previous file with comments | « cipd/client/cipd/client_test.go ('k') | cipd/client/cipd/local/deployer_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The LUCI Authors. All rights reserved. 1 // Copyright 2014 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package local 5 package local
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "io/ioutil" 9 "io/ioutil"
10 "math/rand" 10 "math/rand"
(...skipping 13 matching lines...) Expand all
24 "github.com/luci/luci-go/common/logging" 24 "github.com/luci/luci-go/common/logging"
25 ) 25 )
26 26
27 // TODO(vadimsh): How to handle path conflicts between two packages? Currently 27 // TODO(vadimsh): How to handle path conflicts between two packages? Currently
28 // the last one installed wins. 28 // the last one installed wins.
29 29
30 // TODO(vadimsh): Use some sort of file lock to reduce a chance of corruption. 30 // TODO(vadimsh): Use some sort of file lock to reduce a chance of corruption.
31 31
32 // File system layout of a site directory <base> for "symlink" install method: 32 // File system layout of a site directory <base> for "symlink" install method:
33 // <base>/.cipd/pkgs/ 33 // <base>/.cipd/pkgs/
34 // <package name digest>/ 34 // <arbitrary index>/
35 // description.json
35 // _current -> symlink to fea3ab83440e9dfb813785e16d4101f331ed44f4 36 // _current -> symlink to fea3ab83440e9dfb813785e16d4101f331ed44f4
36 // fea3ab83440e9dfb813785e16d4101f331ed44f4/ 37 // fea3ab83440e9dfb813785e16d4101f331ed44f4/
37 // bin/ 38 // bin/
38 // tool 39 // tool
39 // ... 40 // ...
40 // ... 41 // ...
41 // bin/ 42 // bin/
42 // tool -> symlink to ../.cipd/pkgs/<package name digest>/_current/bin/tool 43 // tool -> symlink to ../.cipd/pkgs/<package name digest>/_current/bin/tool
43 // ... 44 // ...
44 // 45 //
45 // Where <package name digest> is derived from a package name. It doesn't have 46 // Where <arbitrary index> is chosen to be the smallest number available for
46 // to be reversible though, since the package name is still stored in the 47 // this installation (installing more packages gets higher numbers, removing
47 // installed package manifest and can be read from there. 48 // packages and installing new ones will reuse the smallest ones).
Vadim Sh. 2017/01/28 01:50:12 hm... this may be a problem if some updates fail m
iannucci 2017/01/30 02:05:12 I don't think so, actually. Every index folder is
48 // 49 //
49 // Some efforts are made to make sure that during the deployment a window of 50 // Some efforts are made to make sure that during the deployment a window of
50 // inconsistency in the file system is as small as possible. 51 // inconsistency in the file system is as small as possible.
51 // 52 //
52 // For "copy" install method everything is much simpler: files are directly 53 // For "copy" install method everything is much simpler: files are directly
53 // copied to the site root directory and .cipd/pkgs/* contains only metadata, 54 // copied to the site root directory and .cipd/pkgs/* contains only metadata,
54 // such as manifest file with a list of extracted files (to know what to 55 // such as description and manifest files with a list of extracted files (to
55 // uninstall). 56 // know what to uninstall).
56 57
57 // Deployer knows how to unzip and place packages into site root directory. 58 // Deployer knows how to unzip and place packages into site root directory.
58 type Deployer interface { 59 type Deployer interface {
59 // DeployInstance installs an instance of a package into the given subdi r of 60 // DeployInstance installs an instance of a package into the given subdi r of
60 // the root. 61 // the root.
61 // 62 //
62 // It unpacks the package into <base>/.cipd/pkgs/*, and rearranges 63 // It unpacks the package into <base>/.cipd/pkgs/*, and rearranges
63 // symlinks to point to unpacked files. It tries to make it as "atomic" as 64 // symlinks to point to unpacked files. It tries to make it as "atomic" as
64 // possible. Returns information about the deployed instance. 65 // possible. Returns information about the deployed instance.
65 DeployInstance(ctx context.Context, subdir string, inst PackageInstance) (common.Pin, error) 66 DeployInstance(ctx context.Context, subdir string, inst PackageInstance) (common.Pin, error)
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 // currentTxt is a name of a text file with instance ID of latest deployed 136 // currentTxt is a name of a text file with instance ID of latest deployed
136 // version. Used on Windows. 137 // version. Used on Windows.
137 const currentTxt = "_current.txt" 138 const currentTxt = "_current.txt"
138 139
139 // deployerImpl implements Deployer interface. 140 // deployerImpl implements Deployer interface.
140 type deployerImpl struct { 141 type deployerImpl struct {
141 fs FileSystem 142 fs FileSystem
142 } 143 }
143 144
144 func (d *deployerImpl) DeployInstance(ctx context.Context, subdir string, inst P ackageInstance) (common.Pin, error) { 145 func (d *deployerImpl) DeployInstance(ctx context.Context, subdir string, inst P ackageInstance) (common.Pin, error) {
145 » if subdir != "" { 146 » if err := common.ValidateSubdir(subdir); err != nil {
146 » » return common.Pin{}, common.ErrSubdirsNotYetSupported 147 » » return common.Pin{}, err
147 } 148 }
148 149
149 pin := inst.Pin() 150 pin := inst.Pin()
150 » logging.Infof(ctx, "Deploying %s into %s", pin, d.fs.Root()) 151 » logging.Infof(ctx, "Deploying %s into %s(/%s)", pin, d.fs.Root(), subdir )
151 152
152 // Be paranoid. 153 // Be paranoid.
153 if err := common.ValidatePin(pin); err != nil { 154 if err := common.ValidatePin(pin); err != nil {
154 return common.Pin{}, err 155 return common.Pin{}, err
155 } 156 }
156 » if _, err := d.fs.EnsureDirectory(ctx, d.fs.Root()); err != nil { 157 » if _, err := d.fs.EnsureDirectory(ctx, filepath.Join(d.fs.Root(), subdir )); err != nil {
157 return common.Pin{}, err 158 return common.Pin{}, err
158 } 159 }
159 160
160 // Extract new version to the .cipd/pkgs/* guts. For "symlink" install m ode it 161 // Extract new version to the .cipd/pkgs/* guts. For "symlink" install m ode it
161 // is the final destination. For "copy" install mode it's a temp destina tion 162 // is the final destination. For "copy" install mode it's a temp destina tion
162 // and files will be moved to the site root later (in addToSiteRoot call ). 163 // and files will be moved to the site root later (in addToSiteRoot call ).
163 // ExtractPackageInstance knows how to build full paths and how to atomi cally 164 // ExtractPackageInstance knows how to build full paths and how to atomi cally
164 // extract a package. No need to delete garbage if it fails. 165 // extract a package. No need to delete garbage if it fails.
165 pkgPath, err := d.packagePath(ctx, subdir, pin.PackageName, true) 166 pkgPath, err := d.packagePath(ctx, subdir, pin.PackageName, true)
166 if err != nil { 167 if err != nil {
(...skipping 15 matching lines...) Expand all
182 prevManifest := Manifest{} 183 prevManifest := Manifest{}
183 if err == nil && prevInstanceID != "" { 184 if err == nil && prevInstanceID != "" {
184 prevManifest, err = d.readManifest(ctx, filepath.Join(pkgPath, p revInstanceID)) 185 prevManifest, err = d.readManifest(ctx, filepath.Join(pkgPath, p revInstanceID))
185 } 186 }
186 if err != nil { 187 if err != nil {
187 logging.Warningf(ctx, "Previous version of the package is broken : %s", err) 188 logging.Warningf(ctx, "Previous version of the package is broken : %s", err)
188 prevManifest = Manifest{} // to make sure prevManifest.Files == nil. 189 prevManifest = Manifest{} // to make sure prevManifest.Files == nil.
189 } 190 }
190 191
191 // Install all new files to the site root. 192 // Install all new files to the site root.
192 » err = d.addToSiteRoot(ctx, newManifest.Files, newManifest.InstallMode, p kgPath, destPath) 193 » err = d.addToSiteRoot(ctx, subdir, newManifest.Files, newManifest.Instal lMode, pkgPath, destPath)
193 if err != nil { 194 if err != nil {
194 d.fs.EnsureDirectoryGone(ctx, destPath) 195 d.fs.EnsureDirectoryGone(ctx, destPath)
195 return common.Pin{}, err 196 return common.Pin{}, err
196 } 197 }
197 198
198 // Mark installed instance as a current one. After this call the package is 199 // Mark installed instance as a current one. After this call the package is
199 // considered installed and the function must not fail. All cleanup belo w is 200 // considered installed and the function must not fail. All cleanup belo w is
200 // best effort. 201 // best effort.
201 if err = d.setCurrentInstanceID(ctx, pkgPath, pin.InstanceID); err != ni l { 202 if err = d.setCurrentInstanceID(ctx, pkgPath, pin.InstanceID); err != ni l {
202 d.fs.EnsureDirectoryGone(ctx, destPath) 203 d.fs.EnsureDirectoryGone(ctx, destPath)
(...skipping 21 matching lines...) Expand all
224 toKeep := map[string]bool{} 225 toKeep := map[string]bool{}
225 for _, f := range newManifest.Files { 226 for _, f := range newManifest.Files {
226 toKeep[f.Name] = true 227 toKeep[f.Name] = true
227 } 228 }
228 toKill := []FileInfo{} 229 toKill := []FileInfo{}
229 for _, f := range prevManifest.Files { 230 for _, f := range prevManifest.Files {
230 if !toKeep[f.Name] { 231 if !toKeep[f.Name] {
231 toKill = append(toKill, f) 232 toKill = append(toKill, f)
232 } 233 }
233 } 234 }
234 » » » d.removeFromSiteRoot(ctx, toKill) 235 » » » d.removeFromSiteRoot(ctx, subdir, toKill)
235 }() 236 }()
236 } 237 }
237 238
238 // Verify it's all right. 239 // Verify it's all right.
239 newPin, err := d.CheckDeployed(ctx, subdir, pin.PackageName) 240 newPin, err := d.CheckDeployed(ctx, subdir, pin.PackageName)
240 if err == nil && newPin.InstanceID != pin.InstanceID { 241 if err == nil && newPin.InstanceID != pin.InstanceID {
241 err = fmt.Errorf("other instance (%s) was deployed concurrently" , newPin.InstanceID) 242 err = fmt.Errorf("other instance (%s) was deployed concurrently" , newPin.InstanceID)
242 } 243 }
243 if err == nil { 244 if err == nil {
244 logging.Infof(ctx, "Successfully deployed %s", pin) 245 logging.Infof(ctx, "Successfully deployed %s", pin)
245 } else { 246 } else {
246 logging.Errorf(ctx, "Failed to deploy %s: %s", pin, err) 247 logging.Errorf(ctx, "Failed to deploy %s: %s", pin, err)
247 } 248 }
248 return newPin, err 249 return newPin, err
249 } 250 }
250 251
251 func (d *deployerImpl) CheckDeployed(ctx context.Context, subdir, pkg string) (c ommon.Pin, error) { 252 func (d *deployerImpl) CheckDeployed(ctx context.Context, subdir, pkg string) (c ommon.Pin, error) {
252 » if subdir != "" { 253 » if err := common.ValidateSubdir(subdir); err != nil {
253 » » return common.Pin{}, common.ErrSubdirsNotYetSupported 254 » » return common.Pin{}, err
254 } 255 }
255 256
256 pkgPath, err := d.packagePath(ctx, subdir, pkg, false) 257 pkgPath, err := d.packagePath(ctx, subdir, pkg, false)
257 if err != nil { 258 if err != nil {
258 return common.Pin{}, err 259 return common.Pin{}, err
259 } 260 }
260 if pkgPath == "" { 261 if pkgPath == "" {
261 return common.Pin{}, fmt.Errorf("package %s is not installed", p kg) 262 return common.Pin{}, fmt.Errorf("package %s is not installed", p kg)
262 } 263 }
263 264
(...skipping 14 matching lines...) Expand all
278 // Directories with packages are direct children of .cipd/pkgs/. 279 // Directories with packages are direct children of .cipd/pkgs/.
279 pkgs := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir)) 280 pkgs := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir))
280 infos, err := ioutil.ReadDir(pkgs) 281 infos, err := ioutil.ReadDir(pkgs)
281 if err != nil { 282 if err != nil {
282 if os.IsNotExist(err) { 283 if os.IsNotExist(err) {
283 return nil, nil 284 return nil, nil
284 } 285 }
285 return nil, err 286 return nil, err
286 } 287 }
287 288
288 » found := map[string]common.Pin{} 289 » found := common.PinMapBySubdir{}
289 » keys := []string{}
290 for _, info := range infos { 290 for _, info := range infos {
291 if !info.IsDir() { 291 if !info.IsDir() {
292 continue 292 continue
293 } 293 }
294 // Read the description and the 'current' link. 294 // Read the description and the 'current' link.
295 pkgPath := filepath.Join(pkgs, info.Name()) 295 pkgPath := filepath.Join(pkgs, info.Name())
296 desc, err := d.readDescription(ctx, pkgPath) 296 desc, err := d.readDescription(ctx, pkgPath)
297 if err != nil || desc == nil { 297 if err != nil || desc == nil {
298 continue 298 continue
299 } 299 }
300 currentID, err := d.getCurrentInstanceID(pkgPath) 300 currentID, err := d.getCurrentInstanceID(pkgPath)
301 if err != nil || currentID == "" { 301 if err != nil || currentID == "" {
302 continue 302 continue
303 } 303 }
304 304
305 // Ignore duplicate entries, they can appear if someone messes w ith pkgs/* 305 // Ignore duplicate entries, they can appear if someone messes w ith pkgs/*
306 // structure manually. 306 // structure manually.
307 » » if _, ok := found[desc.PackageName]; !ok { 307 » » if _, ok := found[desc.Subdir][desc.PackageName]; !ok {
308 » » » keys = append(keys, desc.PackageName) 308 » » » if _, ok := found[desc.Subdir]; !ok {
309 » » » found[desc.PackageName] = common.Pin{ 309 » » » » found[desc.Subdir] = common.PinMap{}
310 » » » » PackageName: desc.PackageName,
311 » » » » InstanceID: currentID,
312 } 310 }
311 found[desc.Subdir][desc.PackageName] = currentID
313 } 312 }
314 } 313 }
315 314
316 » // Sort by package name. 315 » return found.ToSlice(), nil
317 » sort.Strings(keys)
318 » out := make(common.PinSlice, len(found))
319 » for i, k := range keys {
320 » » out[i] = found[k]
321 » }
322 » return common.PinSliceBySubdir{"": out}, nil
323 } 316 }
324 317
325 func (d *deployerImpl) RemoveDeployed(ctx context.Context, subdir, packageName s tring) error { 318 func (d *deployerImpl) RemoveDeployed(ctx context.Context, subdir, packageName s tring) error {
326 » if subdir != "" { 319 » if err := common.ValidateSubdir(subdir); err != nil {
327 » » return common.ErrSubdirsNotYetSupported 320 » » return err
328 } 321 }
329 322
330 » logging.Infof(ctx, "Removing %s from %s", packageName, d.fs.Root()) 323 » logging.Infof(ctx, "Removing %s from %s(/%s)", packageName, d.fs.Root(), subdir)
331 if err := common.ValidatePackageName(packageName); err != nil { 324 if err := common.ValidatePackageName(packageName); err != nil {
332 return err 325 return err
333 } 326 }
334 pkgPath, err := d.packagePath(ctx, subdir, packageName, false) 327 pkgPath, err := d.packagePath(ctx, subdir, packageName, false)
335 if err != nil { 328 if err != nil {
336 return err 329 return err
337 } 330 }
338 if pkgPath == "" { 331 if pkgPath == "" {
339 logging.Warningf(ctx, "Package %s not found", packageName) 332 logging.Warningf(ctx, "Package %s not found", packageName)
340 return nil 333 return nil
341 } 334 }
342 335
343 // Read the manifest of the currently installed version. 336 // Read the manifest of the currently installed version.
344 manifest := Manifest{} 337 manifest := Manifest{}
345 currentID, err := d.getCurrentInstanceID(pkgPath) 338 currentID, err := d.getCurrentInstanceID(pkgPath)
346 if err == nil && currentID != "" { 339 if err == nil && currentID != "" {
347 manifest, err = d.readManifest(ctx, filepath.Join(pkgPath, curre ntID)) 340 manifest, err = d.readManifest(ctx, filepath.Join(pkgPath, curre ntID))
348 } 341 }
349 342
350 // Warn, but continue with removal anyway. EnsureDirectoryGone call belo w 343 // Warn, but continue with removal anyway. EnsureDirectoryGone call belo w
351 // will nuke everything (even if it's half broken). 344 // will nuke everything (even if it's half broken).
352 if err != nil { 345 if err != nil {
353 logging.Warningf(ctx, "Package %s is in a broken state: %s", pac kageName, err) 346 logging.Warningf(ctx, "Package %s is in a broken state: %s", pac kageName, err)
354 } else { 347 } else {
355 » » d.removeFromSiteRoot(ctx, manifest.Files) 348 » » d.removeFromSiteRoot(ctx, subdir, manifest.Files)
356 } 349 }
357 return d.fs.EnsureDirectoryGone(ctx, pkgPath) 350 return d.fs.EnsureDirectoryGone(ctx, pkgPath)
358 } 351 }
359 352
360 func (d *deployerImpl) TempFile(ctx context.Context, prefix string) (*os.File, e rror) { 353 func (d *deployerImpl) TempFile(ctx context.Context, prefix string) (*os.File, e rror) {
361 dir, err := d.fs.EnsureDirectory(ctx, filepath.Join(d.fs.Root(), SiteSer viceDir, "tmp")) 354 dir, err := d.fs.EnsureDirectory(ctx, filepath.Join(d.fs.Root(), SiteSer viceDir, "tmp"))
362 if err != nil { 355 if err != nil {
363 return nil, err 356 return nil, err
364 } 357 }
365 return ioutil.TempFile(dir, prefix) 358 return ioutil.TempFile(dir, prefix)
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
410 // packagePath returns a path to a package directory in .cipd/pkgs/. 403 // packagePath returns a path to a package directory in .cipd/pkgs/.
411 // 404 //
412 // This will scan all directories under pkgs, looking for a description.json. If 405 // This will scan all directories under pkgs, looking for a description.json. If
413 // an old-style package folder is encountered (e.g. has an instance folder and 406 // an old-style package folder is encountered (e.g. has an instance folder and
414 // current manifest, but doesn't have a description.json), the description.json 407 // current manifest, but doesn't have a description.json), the description.json
415 // will be added. 408 // will be added.
416 // 409 //
417 // If no suitable path is found and allocate is true, this will create a new 410 // If no suitable path is found and allocate is true, this will create a new
418 // directory with an accompanying description.json. Otherwise this returns "". 411 // directory with an accompanying description.json. Otherwise this returns "".
419 func (d *deployerImpl) packagePath(ctx context.Context, subdir, pkg string, allo cate bool) (string, error) { 412 func (d *deployerImpl) packagePath(ctx context.Context, subdir, pkg string, allo cate bool) (string, error) {
413 if err := common.ValidateSubdir(subdir); err != nil {
414 return "", err
415 }
416
420 if err := common.ValidatePackageName(pkg); err != nil { 417 if err := common.ValidatePackageName(pkg); err != nil {
421 return "", err 418 return "", err
422 } 419 }
423 420
424 rel := filepath.Join(filepath.FromSlash(packagesDir)) 421 rel := filepath.Join(filepath.FromSlash(packagesDir))
Vadim Sh. 2017/01/28 01:50:12 er... why do we do filepath.Join here?
iannucci 2017/01/31 01:08:41 dunno!
425 abs, err := d.fs.RootRelToAbs(rel) 422 abs, err := d.fs.RootRelToAbs(rel)
426 if err != nil { 423 if err != nil {
427 logging.Errorf(ctx, "Can't get absolute path of %q: %s", rel, er r) 424 logging.Errorf(ctx, "Can't get absolute path of %q: %s", rel, er r)
428 return "", err 425 return "", err
429 } 426 }
430 files, err := ioutil.ReadDir(abs) 427 files, err := ioutil.ReadDir(abs)
431 if err != nil && !os.IsNotExist(err) { 428 if err != nil && !os.IsNotExist(err) {
432 logging.Errorf(ctx, "Can't read packages dir %q: %s", abs, err) 429 logging.Errorf(ctx, "Can't read packages dir %q: %s", abs, err)
433 return "", err 430 return "", err
434 } 431 }
435 432
436 seenNumbers := numSet{} 433 seenNumbers := numSet{}
437 434
438 for _, f := range files { 435 for _, f := range files {
439 // keep track of all numeric children of .cipd/pkgs 436 // keep track of all numeric children of .cipd/pkgs
440 if n, err := strconv.Atoi(f.Name()); err == nil { 437 if n, err := strconv.Atoi(f.Name()); err == nil {
441 seenNumbers.addNum(n) 438 seenNumbers.addNum(n)
442 } 439 }
443 440
444 fullPkgPath := filepath.Join(abs, f.Name()) 441 fullPkgPath := filepath.Join(abs, f.Name())
445 description, err := d.readDescription(ctx, fullPkgPath) 442 description, err := d.readDescription(ctx, fullPkgPath)
446 if err != nil { 443 if err != nil {
447 logging.Warningf(ctx, "Skipping %q: %s", fullPkgPath, er r) 444 logging.Warningf(ctx, "Skipping %q: %s", fullPkgPath, er r)
448 continue 445 continue
449 } 446 }
450 » » if description.PackageName == pkg { 447 » » if description.PackageName == pkg && description.Subdir == subdi r {
451 return fullPkgPath, nil 448 return fullPkgPath, nil
452 } 449 }
453 } 450 }
454 451
455 if !allocate { 452 if !allocate {
456 return "", nil 453 return "", nil
457 } 454 }
458 455
459 // we didn't find one, so we have to make one 456 // we didn't find one, so we have to make one
460 if _, err := d.fs.EnsureDirectory(ctx, abs); err != nil { 457 if _, err := d.fs.EnsureDirectory(ctx, abs); err != nil {
461 logging.Errorf(ctx, "Cannot ensure packages directory: %s", err) 458 logging.Errorf(ctx, "Cannot ensure packages directory: %s", err)
462 return "", err 459 return "", err
463 } 460 }
464 461
465 tmpDir, err := d.TempDir(ctx, strings.Replace(pkg, "/", "_", -1)) 462 tmpDir, err := d.TempDir(ctx, strings.Replace(pkg, "/", "_", -1))
Vadim Sh. 2017/01/28 01:50:12 using 'pkg' here can be a problem on windows. We f
iannucci 2017/01/31 01:08:41 done, I have it take the last two components now.
466 if err != nil { 463 if err != nil {
467 logging.Errorf(ctx, "Cannot create new pkg tempdir: %s", err) 464 logging.Errorf(ctx, "Cannot create new pkg tempdir: %s", err)
468 return "", err 465 return "", err
469 } 466 }
470 defer d.fs.EnsureDirectoryGone(ctx, tmpDir) 467 defer d.fs.EnsureDirectoryGone(ctx, tmpDir)
471 err = d.fs.EnsureFile(ctx, filepath.Join(tmpDir, descriptionName), func( f *os.File) error { 468 err = d.fs.EnsureFile(ctx, filepath.Join(tmpDir, descriptionName), func( f *os.File) error {
472 return writeDescription(&Description{Subdir: subdir, PackageName : pkg}, f) 469 return writeDescription(&Description{Subdir: subdir, PackageName : pkg}, f)
473 }) 470 })
474 if err != nil { 471 if err != nil {
475 logging.Errorf(ctx, "Cannot create new pkg description.json: %s" , err) 472 logging.Errorf(ctx, "Cannot create new pkg description.json: %s" , err)
(...skipping 27 matching lines...) Expand all
503 return "", err 500 return "", err
504 } 501 }
505 502
506 // rename failed with ENOTEMPTY, that means that another client wrote this 503 // rename failed with ENOTEMPTY, that means that another client wrote this
507 // directory. 504 // directory.
508 description, err := d.readDescription(ctx, pkgPath) 505 description, err := d.readDescription(ctx, pkgPath)
509 if err != nil { 506 if err != nil {
510 logging.Warningf(ctx, "Skipping %q: %s", pkgPath, err) 507 logging.Warningf(ctx, "Skipping %q: %s", pkgPath, err)
511 continue 508 continue
512 } 509 }
513 » » if description.PackageName == pkg { 510 » » if description.PackageName == pkg && description.Subdir == subdi r {
514 return pkgPath, nil 511 return pkgPath, nil
515 } 512 }
516 } 513 }
517 514
518 logging.Errorf(ctx, "Unable to find valid index for package %q in %s!", pkg, abs) 515 logging.Errorf(ctx, "Unable to find valid index for package %q in %s!", pkg, abs)
519 return "", err 516 return "", err
520 } 517 }
521 518
522 // getCurrentInstanceID returns instance ID of currently installed instance 519 // getCurrentInstanceID returns instance ID of currently installed instance
523 // given a path to a package directory (.cipd/pkgs/<name>). 520 // given a path to a package directory (.cipd/pkgs/<name>).
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
638 if len(manifest.Files) == 0 { 635 if len(manifest.Files) == 0 {
639 if manifest.Files, err = scanPackageDir(ctx, instanceDir); err ! = nil { 636 if manifest.Files, err = scanPackageDir(ctx, instanceDir); err ! = nil {
640 return Manifest{}, err 637 return Manifest{}, err
641 } 638 }
642 } 639 }
643 return manifest, nil 640 return manifest, nil
644 } 641 }
645 642
646 // addToSiteRoot moves or symlinks files into the site root directory (depending 643 // addToSiteRoot moves or symlinks files into the site root directory (depending
647 // on passed installMode). 644 // on passed installMode).
648 func (d *deployerImpl) addToSiteRoot(ctx context.Context, files []FileInfo, inst allMode InstallMode, pkgDir, srcDir string) error { 645 func (d *deployerImpl) addToSiteRoot(ctx context.Context, subdir string, files [ ]FileInfo, installMode InstallMode, pkgDir, srcDir string) error {
649 // On Windows only InstallModeCopy is supported. 646 // On Windows only InstallModeCopy is supported.
650 if runtime.GOOS == "windows" { 647 if runtime.GOOS == "windows" {
651 installMode = InstallModeCopy 648 installMode = InstallModeCopy
652 } else if installMode == "" { 649 } else if installMode == "" {
653 installMode = InstallModeSymlink // default on non-Windows 650 installMode = InstallModeSymlink // default on non-Windows
654 } 651 }
655 if err := ValidateInstallMode(installMode); err != nil { 652 if err := ValidateInstallMode(installMode); err != nil {
656 return err 653 return err
657 } 654 }
658 655
659 for _, f := range files { 656 for _, f := range files {
660 » » // E.g. bin/tool. 657 » » // e.g. bin/tool
661 relPath := filepath.FromSlash(f.Name) 658 relPath := filepath.FromSlash(f.Name)
662 » » destAbs, err := d.fs.RootRelToAbs(relPath) 659 » » // e.g. <base>/<subdir>/bin/tool
660 » » destAbs, err := d.fs.RootRelToAbs(filepath.Join(subdir, relPath) )
663 if err != nil { 661 if err != nil {
664 logging.Warningf(ctx, "Invalid relative path %q: %s", re lPath, err) 662 logging.Warningf(ctx, "Invalid relative path %q: %s", re lPath, err)
665 return err 663 return err
666 } 664 }
667 if installMode == InstallModeSymlink { 665 if installMode == InstallModeSymlink {
668 » » » // E.g. <base>/.cipd/pkgs/name/_current/bin/tool. 666 » » » // e.g. <base>/.cipd/pkgs/name/_current/bin/tool
669 targetAbs := filepath.Join(pkgDir, currentSymlink, relPa th) 667 targetAbs := filepath.Join(pkgDir, currentSymlink, relPa th)
670 » » » // E.g. ../.cipd/pkgs/name/_current/bin/tool. 668 » » » // e.g. ../.cipd/pkgs/name/_current/bin/tool
669 » » » // has more `../` depending on subdir
671 targetRel, err := filepath.Rel(filepath.Dir(destAbs), ta rgetAbs) 670 targetRel, err := filepath.Rel(filepath.Dir(destAbs), ta rgetAbs)
672 if err != nil { 671 if err != nil {
673 logging.Warningf( 672 logging.Warningf(
674 ctx, "Can't get relative path from %s to %s", 673 ctx, "Can't get relative path from %s to %s",
675 filepath.Dir(destAbs), targetAbs) 674 filepath.Dir(destAbs), targetAbs)
676 return err 675 return err
677 } 676 }
678 if err = d.fs.EnsureSymlink(ctx, destAbs, targetRel); er r != nil { 677 if err = d.fs.EnsureSymlink(ctx, destAbs, targetRel); er r != nil {
679 logging.Warningf(ctx, "Failed to create symlink for %s", relPath) 678 logging.Warningf(ctx, "Failed to create symlink for %s", relPath)
680 return err 679 return err
681 } 680 }
682 } else if installMode == InstallModeCopy { 681 } else if installMode == InstallModeCopy {
683 // E.g. <base>/.cipd/pkgs/name/<id>/bin/tool. 682 // E.g. <base>/.cipd/pkgs/name/<id>/bin/tool.
684 srcAbs := filepath.Join(srcDir, relPath) 683 srcAbs := filepath.Join(srcDir, relPath)
685 if err := d.fs.Replace(ctx, srcAbs, destAbs); err != nil { 684 if err := d.fs.Replace(ctx, srcAbs, destAbs); err != nil {
686 logging.Warningf(ctx, "Failed to move %s to %s: %s", srcAbs, destAbs, err) 685 logging.Warningf(ctx, "Failed to move %s to %s: %s", srcAbs, destAbs, err)
687 return err 686 return err
688 } 687 }
689 } else { 688 } else {
690 // Should not happen. ValidateInstallMode checks this. 689 // Should not happen. ValidateInstallMode checks this.
691 return fmt.Errorf("impossible state") 690 return fmt.Errorf("impossible state")
692 } 691 }
693 } 692 }
694 » // Best effort cleanup of empty directories after all files has been mov ed. 693 » // Best effort cleanup of empty directories after all files have been mo ved.
695 if installMode == InstallModeCopy { 694 if installMode == InstallModeCopy {
696 d.removeEmptyDirs(ctx, srcDir) 695 d.removeEmptyDirs(ctx, srcDir)
697 } 696 }
698 return nil 697 return nil
699 } 698 }
700 699
701 // removeFromSiteRoot deletes files from the site root directory. 700 // removeFromSiteRoot deletes files from the site root directory.
702 // 701 //
703 // Best effort. Logs errors and carries on. 702 // Best effort. Logs errors and carries on.
704 func (d *deployerImpl) removeFromSiteRoot(ctx context.Context, files []FileInfo) { 703 func (d *deployerImpl) removeFromSiteRoot(ctx context.Context, subdir string, fi les []FileInfo) {
705 for _, f := range files { 704 for _, f := range files {
706 » » absPath, err := d.fs.RootRelToAbs(filepath.FromSlash(f.Name)) 705 » » absPath, err := d.fs.RootRelToAbs(filepath.Join(subdir, filepath .FromSlash(f.Name)))
706 » » fmt.Printf("wat: %q, %q, %q\n", subdir, files[0].Name, absPath)
Vadim Sh. 2017/01/28 01:50:12 wat? :)
iannucci 2017/01/31 01:08:41 :D
707 if err != nil { 707 if err != nil {
708 logging.Warningf(ctx, "Refusing to remove %q: %s", f.Nam e, err) 708 logging.Warningf(ctx, "Refusing to remove %q: %s", f.Nam e, err)
709 continue 709 continue
710 } 710 }
711 if err := d.fs.EnsureFileGone(ctx, absPath); err != nil { 711 if err := d.fs.EnsureFileGone(ctx, absPath); err != nil {
712 logging.Warningf(ctx, "Failed to remove a file from the site root: %s", err) 712 logging.Warningf(ctx, "Failed to remove a file from the site root: %s", err)
713 } 713 }
714 } 714 }
715 d.removeEmptyDirs(ctx, d.fs.Root()) 715 d.removeEmptyDirs(ctx, d.fs.Root())
716 } 716 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
760 Size: uint64(info.Size()), 760 Size: uint64(info.Size()),
761 Executable: (info.Mode().Perm() & 0111) != 0, 761 Executable: (info.Mode().Perm() & 0111) != 0,
762 Symlink: symlink, 762 Symlink: symlink,
763 }) 763 })
764 } 764 }
765 } 765 }
766 return nil 766 return nil
767 }) 767 })
768 return out, err 768 return out, err
769 } 769 }
OLDNEW
« no previous file with comments | « cipd/client/cipd/client_test.go ('k') | cipd/client/cipd/local/deployer_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698