2016-12-23 17:49:24 +03:00
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
package dep
|
2016-12-23 17:49:24 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
2016-12-23 18:34:20 +03:00
|
|
|
"github.com/pkg/errors"
|
2016-12-23 17:49:24 +03:00
|
|
|
"github.com/sdboyer/gps"
|
|
|
|
)
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
// SafeWriter transactionalizes writes of manifest, lock, and vendor dir, both
|
2016-12-23 17:49:24 +03:00
|
|
|
// individually and in any combination, into a pseudo-atomic action with
|
|
|
|
// transactional rollback.
|
|
|
|
//
|
|
|
|
// It is not impervious to errors (writing to disk is hard), but it should
|
|
|
|
// guard against non-arcane failure conditions.
|
2017-01-27 23:39:07 +03:00
|
|
|
type SafeWriter struct {
|
|
|
|
Root string // absolute path of root dir in which to write
|
|
|
|
Manifest *Manifest // the manifest to write, if any
|
|
|
|
Lock *Lock // the old lock, if any
|
|
|
|
NewLock gps.Lock // the new lock, if any
|
|
|
|
SourceManager gps.SourceManager
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
// WriteAllSafe writes out some combination of config yaml, lock, and a vendor
|
2016-12-23 17:49:24 +03:00
|
|
|
// tree, to a temp dir, then moves them into place if and only if all the write
|
|
|
|
// operations succeeded. It also does its best to roll back if any moves fail.
|
|
|
|
//
|
2017-01-19 03:11:38 +03:00
|
|
|
// This mostly guarantees that dep cannot exit with a partial write that would
|
2016-12-30 06:15:23 +03:00
|
|
|
// leave an undefined state on disk.
|
2016-12-23 17:49:24 +03:00
|
|
|
//
|
2017-01-27 23:39:07 +03:00
|
|
|
// - If a sw.Manifest is provided, it will be written to the standard manifest file
|
|
|
|
// name beneath sw.Root
|
|
|
|
// - If sw.Lock is provided without an sw.NewLock, it will be written to the standard
|
2016-12-30 06:56:11 +03:00
|
|
|
// lock file name in the root dir, but vendor will NOT be written
|
2017-01-27 23:39:07 +03:00
|
|
|
// - If sw.Lock and sw.NewLock are both provided and are equivalent, then neither lock
|
2016-12-30 06:15:23 +03:00
|
|
|
// nor vendor will be written
|
2017-01-27 23:39:07 +03:00
|
|
|
// - If sw.Lock and sw.NewLock are both provided and are not equivalent,
|
2016-12-23 17:49:24 +03:00
|
|
|
// the nl will be written to the same location as above, and a vendor
|
2017-01-27 23:39:07 +03:00
|
|
|
// tree will be written to sw.Root/vendor
|
|
|
|
// - If sw.NewLock is provided and sw.Lockock is not, it will write both a lock
|
2016-12-23 17:49:24 +03:00
|
|
|
// and vendor dir in the same way
|
2016-12-30 06:15:23 +03:00
|
|
|
// - If the forceVendor param is true, then vendor will be unconditionally
|
2017-01-27 23:39:07 +03:00
|
|
|
// written out based on sw.NewLock if present, else sw.Lock, else error.
|
2016-12-23 17:49:24 +03:00
|
|
|
//
|
2016-12-30 06:15:23 +03:00
|
|
|
// Any of m, l, or nl can be omitted; the grouped write operation will continue
|
|
|
|
// for whichever inputs are present. A SourceManager is only required if vendor
|
|
|
|
// is being written.
|
2017-01-27 23:39:07 +03:00
|
|
|
func (sw SafeWriter) WriteAllSafe(forceVendor bool) error {
|
2016-12-23 17:49:24 +03:00
|
|
|
// Decide which writes we need to do
|
2016-12-23 18:34:20 +03:00
|
|
|
var writeM, writeL, writeV bool
|
2016-12-30 06:56:11 +03:00
|
|
|
writeV = forceVendor
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
if sw.Manifest != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
writeM = true
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
if sw.NewLock != nil {
|
|
|
|
if sw.Lock == nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
writeL, writeV = true, true
|
2016-12-23 17:49:24 +03:00
|
|
|
} else {
|
2017-01-27 23:39:07 +03:00
|
|
|
rlf := LockFromInterface(sw.NewLock)
|
|
|
|
if !locksAreEquivalent(rlf, sw.Lock) {
|
2016-12-23 18:34:20 +03:00
|
|
|
writeL, writeV = true, true
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
2017-01-27 23:39:07 +03:00
|
|
|
} else if sw.Lock != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
writeL = true
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
if sw.Root == "" {
|
2016-12-30 06:15:23 +03:00
|
|
|
return errors.New("root path must be non-empty")
|
|
|
|
}
|
2017-01-27 23:39:07 +03:00
|
|
|
if is, err := IsDir(sw.Root); !is {
|
2016-12-30 06:15:23 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-27 23:39:07 +03:00
|
|
|
return fmt.Errorf("root path %q does not exist", sw.Root)
|
2016-12-30 06:15:23 +03:00
|
|
|
}
|
|
|
|
|
2016-12-23 18:34:20 +03:00
|
|
|
if !writeM && !writeL && !writeV {
|
2016-12-23 17:49:24 +03:00
|
|
|
// nothing to do
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
if writeV && sw.SourceManager == nil {
|
2017-01-25 00:48:14 +03:00
|
|
|
return errors.New("must provide a SourceManager if writing out a vendor dir")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
if writeV && sw.Lock == nil && sw.NewLock == nil {
|
2016-12-30 08:39:08 +03:00
|
|
|
return errors.New("must provide a lock in order to write out vendor")
|
|
|
|
}
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
mpath := filepath.Join(sw.Root, ManifestName)
|
|
|
|
lpath := filepath.Join(sw.Root, LockName)
|
|
|
|
vpath := filepath.Join(sw.Root, "vendor")
|
2016-12-30 06:56:11 +03:00
|
|
|
|
2017-01-19 03:11:38 +03:00
|
|
|
td, err := ioutil.TempDir(os.TempDir(), "dep")
|
2016-12-23 17:49:24 +03:00
|
|
|
if err != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
return errors.Wrap(err, "error while creating temp dir for writing manifest/lock/vendor")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
|
2016-12-23 18:34:20 +03:00
|
|
|
if writeM {
|
2017-01-27 23:39:07 +03:00
|
|
|
if err := writeFile(filepath.Join(td, ManifestName), sw.Manifest); err != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
return errors.Wrap(err, "failed to write manifest file to temp dir")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 18:34:20 +03:00
|
|
|
if writeL {
|
2017-01-27 23:39:07 +03:00
|
|
|
if sw.NewLock == nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
// the new lock is nil but the flag is on, so we must be writing
|
2016-12-23 17:49:24 +03:00
|
|
|
// the other one
|
2017-01-27 23:39:07 +03:00
|
|
|
if err := writeFile(filepath.Join(td, LockName), sw.Lock); err != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
return errors.Wrap(err, "failed to write lock file to temp dir")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
} else {
|
2017-01-27 23:39:07 +03:00
|
|
|
rlf := LockFromInterface(sw.NewLock)
|
|
|
|
if err := writeFile(filepath.Join(td, LockName), rlf); err != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
return errors.Wrap(err, "failed to write lock file to temp dir")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 18:34:20 +03:00
|
|
|
if writeV {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Prefer the nl, but take the l if only that's available, as could be the
|
|
|
|
// case if true was passed for forceVendor.
|
2017-01-27 23:39:07 +03:00
|
|
|
l := sw.NewLock
|
2016-12-30 08:39:08 +03:00
|
|
|
if l == nil {
|
2017-01-27 23:39:07 +03:00
|
|
|
l = sw.Lock
|
2016-12-30 08:39:08 +03:00
|
|
|
}
|
2017-01-27 23:39:07 +03:00
|
|
|
err = gps.WriteDepTree(filepath.Join(td, "vendor"), l, sw.SourceManager, true)
|
2016-12-23 17:49:24 +03:00
|
|
|
if err != nil {
|
2016-12-30 06:15:23 +03:00
|
|
|
return errors.Wrap(err, "error while writing out vendor tree")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move the existing files and dirs to the temp dir while we put the new
|
2017-01-25 10:29:04 +03:00
|
|
|
// ones in, to provide insurance against errors for as long as possible.
|
2016-12-23 17:49:24 +03:00
|
|
|
type pathpair struct {
|
|
|
|
from, to string
|
|
|
|
}
|
|
|
|
var restore []pathpair
|
2016-12-30 06:56:11 +03:00
|
|
|
var failerr error
|
|
|
|
var vendorbak string
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2016-12-23 18:34:20 +03:00
|
|
|
if writeM {
|
2016-12-30 06:15:23 +03:00
|
|
|
if _, err := os.Stat(mpath); err == nil {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move out the old one.
|
2017-01-27 23:39:07 +03:00
|
|
|
tmploc := filepath.Join(td, ManifestName+".orig")
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(mpath, tmploc)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-30 06:56:11 +03:00
|
|
|
restore = append(restore, pathpair{from: tmploc, to: mpath})
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move in the new one.
|
2017-01-27 23:39:07 +03:00
|
|
|
failerr = renameWithFallback(filepath.Join(td, ManifestName), mpath)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-30 06:56:11 +03:00
|
|
|
if writeL {
|
2016-12-30 06:15:23 +03:00
|
|
|
if _, err := os.Stat(lpath); err == nil {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move out the old one.
|
2017-01-27 23:39:07 +03:00
|
|
|
tmploc := filepath.Join(td, LockName+".orig")
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(lpath, tmploc)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-30 06:56:11 +03:00
|
|
|
restore = append(restore, pathpair{from: tmploc, to: lpath})
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move in the new one.
|
2017-01-27 23:39:07 +03:00
|
|
|
failerr = renameWithFallback(filepath.Join(td, LockName), lpath)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-30 06:56:11 +03:00
|
|
|
if writeV {
|
2016-12-30 06:15:23 +03:00
|
|
|
if _, err := os.Stat(vpath); err == nil {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move out the old vendor dir. just do it into an adjacent dir, to
|
2016-12-23 18:34:20 +03:00
|
|
|
// try to mitigate the possibility of a pointless cross-filesystem
|
2017-01-25 10:29:04 +03:00
|
|
|
// move with a temp directory.
|
2016-12-30 06:15:23 +03:00
|
|
|
vendorbak = vpath + ".orig"
|
2016-12-23 17:49:24 +03:00
|
|
|
if _, err := os.Stat(vendorbak); err == nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
// If the adjacent dir already exists, bite the bullet and move
|
2017-01-25 10:29:04 +03:00
|
|
|
// to a proper tempdir.
|
2016-12-23 18:34:20 +03:00
|
|
|
vendorbak = filepath.Join(td, "vendor.orig")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-23 18:34:20 +03:00
|
|
|
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(vpath, vendorbak)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-30 06:56:11 +03:00
|
|
|
restore = append(restore, pathpair{from: vendorbak, to: vpath})
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move in the new one.
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(filepath.Join(td, "vendor"), vpath)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Renames all went smoothly. The deferred os.RemoveAll will get the temp
|
|
|
|
// dir, but if we wrote vendor, we have to clean that up directly
|
2016-12-23 18:34:20 +03:00
|
|
|
if writeV {
|
2016-12-30 06:56:11 +03:00
|
|
|
// Nothing we can really do about an error at this point, so ignore it
|
2016-12-23 17:49:24 +03:00
|
|
|
os.RemoveAll(vendorbak)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-12-30 06:56:11 +03:00
|
|
|
|
|
|
|
fail:
|
2017-01-25 10:29:04 +03:00
|
|
|
// If we failed at any point, move all the things back into place, then bail.
|
2016-12-30 06:56:11 +03:00
|
|
|
for _, pair := range restore {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Nothing we can do on err here, as we're already in recovery mode.
|
2017-01-01 04:11:05 +03:00
|
|
|
renameWithFallback(pair.from, pair.to)
|
2016-12-30 06:56:11 +03:00
|
|
|
}
|
|
|
|
return failerr
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|