Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |