Clean up and improve txn writer logic a bit more

This commit is contained in:
sam boyer 2016-12-29 22:56:11 -05:00 коммит произвёл Jess Frazelle
Родитель 14dc3dae26
Коммит 31e7a7599f
1 изменённых файлов: 33 добавлений и 38 удалений

Просмотреть файл

@ -37,8 +37,8 @@ type safeWriter struct {
// //
// - If a sw.m is provided, it will be written to the standard manifest file // - If a sw.m is provided, it will be written to the standard manifest file
// name beneath sw.root // name beneath sw.root
// - If sw.l is provided without an sw.nl, it will be unconditionally written // - If sw.l is provided without an sw.nl, it will be written to the standard
// to the standard lock file name in the root dir // lock file name in the root dir, but vendor will NOT be written
// - If sw.l and sw.nl are both provided and are equivalent, then neither lock // - If sw.l and sw.nl are both provided and are equivalent, then neither lock
// nor vendor will be written // nor vendor will be written
// - If sw.l and sw.nl are both provided and are not equivalent, // - If sw.l and sw.nl are both provided and are not equivalent,
@ -55,6 +55,7 @@ type safeWriter struct {
func (sw safeWriter) writeAllSafe(forceVendor bool) error { func (sw safeWriter) writeAllSafe(forceVendor bool) error {
// Decide which writes we need to do // Decide which writes we need to do
var writeM, writeL, writeV bool var writeM, writeL, writeV bool
writeV = forceVendor
if sw.m != nil { if sw.m != nil {
writeM = true writeM = true
@ -83,23 +84,23 @@ func (sw safeWriter) writeAllSafe(forceVendor bool) error {
return fmt.Errorf("root path %q does not exist", sw.root) return fmt.Errorf("root path %q does not exist", sw.root)
} }
mpath := filepath.Join(sw.root, manifestName)
lpath := filepath.Join(sw.root, lockName)
vpath := filepath.Join(sw.root, "vendor")
if !writeM && !writeL && !writeV { if !writeM && !writeL && !writeV {
// nothing to do // nothing to do
return nil return nil
} }
if (writeL || writeV) && vpath == "" { if writeV && sw.l == nil && sw.nl == nil {
return errors.New("must provide a vendor dir if writing out a lock or vendor dir.") return errors.New("must provide a lock in order to write out vendor")
} }
if writeV && sw.sm == nil { if writeV && sw.sm == nil {
return errors.New("must provide a SourceManager if writing out a vendor dir.") return errors.New("must provide a SourceManager if writing out a vendor dir.")
} }
mpath := filepath.Join(sw.root, manifestName)
lpath := filepath.Join(sw.root, lockName)
vpath := filepath.Join(sw.root, "vendor")
td, err := ioutil.TempDir(os.TempDir(), "dep") td, err := ioutil.TempDir(os.TempDir(), "dep")
if err != nil { if err != nil {
return errors.Wrap(err, "error while creating temp dir for writing manifest/lock/vendor") return errors.Wrap(err, "error while creating temp dir for writing manifest/lock/vendor")
@ -136,12 +137,12 @@ func (sw safeWriter) writeAllSafe(forceVendor bool) error {
// Move the existing files and dirs to the temp dir while we put the new // Move the existing files and dirs to the temp dir while we put the new
// ones in, to provide insurance against errors for as long as possible // ones in, to provide insurance against errors for as long as possible
var fail bool
var failerr error
type pathpair struct { type pathpair struct {
from, to string from, to string
} }
var restore []pathpair var restore []pathpair
var failerr error
var vendorbak string
if writeM { if writeM {
if _, err := os.Stat(mpath); err == nil { if _, err := os.Stat(mpath); err == nil {
@ -149,84 +150,78 @@ func (sw safeWriter) writeAllSafe(forceVendor bool) error {
tmploc := filepath.Join(td, manifestName+".orig") tmploc := filepath.Join(td, manifestName+".orig")
failerr = renameElseCopy(mpath, tmploc) failerr = renameElseCopy(mpath, tmploc)
if failerr != nil { if failerr != nil {
fail = true goto fail
} else {
restore = append(restore, pathpair{from: tmploc, to: mpath})
} }
restore = append(restore, pathpair{from: tmploc, to: mpath})
} }
// move in the new one // move in the new one
failerr = renameElseCopy(filepath.Join(td, manifestName), mpath) failerr = renameElseCopy(filepath.Join(td, manifestName), mpath)
if failerr != nil { if failerr != nil {
fail = true goto fail
} }
} }
if !fail && writeL { if writeL {
if _, err := os.Stat(lpath); err == nil { if _, err := os.Stat(lpath); err == nil {
// move out the old one // move out the old one
tmploc := filepath.Join(td, lockName+".orig") tmploc := filepath.Join(td, lockName+".orig")
failerr = renameElseCopy(lpath, tmploc) failerr = renameElseCopy(lpath, tmploc)
if failerr != nil { if failerr != nil {
fail = true goto fail
} else {
restore = append(restore, pathpair{from: tmploc, to: lpath})
} }
restore = append(restore, pathpair{from: tmploc, to: lpath})
} }
// move in the new one // move in the new one
failerr = renameElseCopy(filepath.Join(td, lockName), lpath) failerr = renameElseCopy(filepath.Join(td, lockName), lpath)
if failerr != nil { if failerr != nil {
fail = true goto fail
} }
} }
// have to declare out here so it's present later if writeV {
var vendorbak string
if !fail && writeV {
if _, err := os.Stat(vpath); err == nil { if _, err := os.Stat(vpath); err == nil {
// move out the old vendor dir. just do it into an adjacent dir, to // move out the old vendor dir. just do it into an adjacent dir, to
// try to mitigate the possibility of a pointless cross-filesystem // try to mitigate the possibility of a pointless cross-filesystem
// move with a temp dir // move with a temp dir
vendorbak = vpath + ".orig" vendorbak = vpath + ".orig"
if _, err := os.Stat(vendorbak); err == nil { if _, err := os.Stat(vendorbak); err == nil {
// If that does already exist bite the bullet and use a proper // If the adjacent dir already exists, bite the bullet and move
// tempdir // to a proper tempdir
vendorbak = filepath.Join(td, "vendor.orig") vendorbak = filepath.Join(td, "vendor.orig")
} }
failerr = renameElseCopy(vpath, vendorbak) failerr = renameElseCopy(vpath, vendorbak)
if failerr != nil { if failerr != nil {
fail = true goto fail
} else {
restore = append(restore, pathpair{from: vendorbak, to: vpath})
} }
restore = append(restore, pathpair{from: vendorbak, to: vpath})
} }
// move in the new one // move in the new one
failerr = renameElseCopy(filepath.Join(td, "vendor"), vpath) failerr = renameElseCopy(filepath.Join(td, "vendor"), vpath)
if failerr != nil { if failerr != nil {
fail = true goto fail
} }
} }
// If we failed at any point, move all the things back into place, then bail
if fail {
for _, pair := range restore {
// Nothing we can do on err here, we're already in recovery mode
renameElseCopy(pair.from, pair.to)
}
return failerr
}
// Renames all went smoothly. The deferred os.RemoveAll will get the temp // 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 // dir, but if we wrote vendor, we have to clean that up directly
if writeV { if writeV {
// Again, kinda nothing we can do about an error at this point // Nothing we can really do about an error at this point, so ignore it
os.RemoveAll(vendorbak) os.RemoveAll(vendorbak)
} }
return nil return nil
fail:
// If we failed at any point, move all the things back into place, then bail
for _, pair := range restore {
// Nothing we can do on err here, as we're already in recovery mode
renameElseCopy(pair.from, pair.to)
}
return failerr
} }