Skip to content

Commit

Permalink
refactor: moving rollback functions into rollback package.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreznel committed Jan 13, 2020
1 parent 6073cdd commit ae2d575
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 174 deletions.
7 changes: 3 additions & 4 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package cmd

import (
"github.com/metrumresearchgroup/pkgr/pacman"
"github.com/metrumresearchgroup/pkgr/rollback"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -76,7 +75,7 @@ func rInstall(cmd *cobra.Command, args []string) error {
}
}

if viper.GetBool("update") {
if viper.GetBool("update") && viper.GetBool("rollback") {
log.Info("update argument passed. staging packages for update...")
rollbackPlan.PreparePackagesForUpdate(fs, cfg.Library)
}
Expand Down Expand Up @@ -111,15 +110,15 @@ func rInstall(cmd *cobra.Command, args []string) error {
err = rcmd.InstallPackagePlan(fs, installPlan, pkgMap, packageCache, pkgInstallArgs, rSettings, rcmd.ExecSettings{PkgrVersion: VERSION}, nworkers)

//If anything went wrong during the installation, rollback the environment.
if err != nil {
if err != nil && viper.GetBool("rollback") {
errRollback := rollback.RollbackPackageEnvironment(fs, rollbackPlan)
if errRollback != nil {
log.WithFields(log.Fields{

}).Error("failed to reset package environment after bad installation. Your package Library will be in a corrupt state. It is recommended you delete your Library and reinstall all packages.")
}
} else {
pacman.CleanUpdateBackups(fs, rollbackPlan.UpdateRollbacks)
rollback.CleanUpdateBackups(fs, rollbackPlan.UpdateRollbacks)
}

log.Info("duration:", time.Since(startTime))
Expand Down
2 changes: 2 additions & 0 deletions cmd/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ func planInstall(rv cran.RVersion, exitOnMissing bool) (*cran.PkgNexus, gpsr.Ins
pkgNexus,
cfg.Update,
libraryExists)


rollbackPlan := rollback.CreateRollbackPlan(cfg.Library, installPlan, installedPackages)

if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,14 @@ func init() {
RootCmd.PersistentFlags().Bool("update", cfg.Update, "Update packages along with install")
_ = viper.BindPFlag("update", RootCmd.PersistentFlags().Lookup("update"))

RootCmd.PersistentFlags().Bool("strict", cfg.Update, "Enable strict mode")
RootCmd.PersistentFlags().Bool("rollback", cfg.Rollback, "Enable rollback")
_ = viper.BindPFlag("update", RootCmd.PersistentFlags().Lookup("rollback"))

RootCmd.PersistentFlags().Bool("strict", cfg.Strict, "Enable strict mode")
_ = viper.BindPFlag("strict", RootCmd.PersistentFlags().Lookup("strict"))

//RootCmd.PersistentFlags().Bool/**/

// packrat related
// RootCmd.PersistentFlags().String("pr_lockfile", "", "packrat lockfile")
// viper.BindPFlag("pr_lockfile", RootCmd.PersistentFlags().Lookup("pr_lockfile"))
Expand Down
1 change: 1 addition & 0 deletions configlib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func loadDefaultSettings() {
viper.SetDefault("rpath", "R")
viper.SetDefault("threads", 0)
viper.SetDefault("strict", false)
viper.SetDefault("rollback", true)
}

// IsCustomizationSet ...
Expand Down
1 change: 1 addition & 0 deletions configlib/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type PkgrConfig struct {
Packages []string `yaml:"Packages,omitempty"`
Suggests bool `yaml:"Suggests,omitempty"`
Repos []map[string]string `yaml:"Repos,omitempty"`
Rollback bool `yaml:"Rollback,omitempty"`
Library string `yaml:"Library,omitempty"`
LibPaths []string `yaml:"LibPaths,omitempty"`
Customizations Customizations `yaml:"Customizations,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/rollback-disabled/guide.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# rollback
tags: rollback
# rollback-disabled
tags: rollback-disabled

## Description
Testing area to demonstrate that the entire package environment is rolled back
Expand Down
108 changes: 0 additions & 108 deletions pacman/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,114 +132,6 @@ func GetOutdatedPackages(installed map[string]desc.Desc, availablePackages []cra
return outdatedPackages
}

// PreparePackagesForUpdate ...
func PreparePackagesForUpdate(fileSystem afero.Fs, libraryPath string, outdatedPackages []cran.OutdatedPackage) []UpdateAttempt {
var updateAttempts []UpdateAttempt

//Tag each outdated pkg directory in library with "__OLD__"
for _, pkg := range outdatedPackages {
updateAttempts = append(updateAttempts, tagOldInstallation(fileSystem, libraryPath, pkg))
}

return updateAttempts
}

func tagOldInstallation(fileSystem afero.Fs, libraryPath string, outdatedPackage cran.OutdatedPackage) UpdateAttempt {
packageDir := filepath.Join(libraryPath, outdatedPackage.Package)
taggedPackageDir := filepath.Join(libraryPath, "__OLD__"+outdatedPackage.Package)

err := fileSystem.Rename(packageDir, taggedPackageDir)
//err := RenameDirRecursive(fileSystem, packageDir, taggedPackageDir)

if err != nil {
log.WithField("package dir", packageDir).Warn("error when backing up outdated package")
log.Error(err)
}

return UpdateAttempt{
Package: outdatedPackage.Package,
ActivePackageDirectory: packageDir,
BackupPackageDirectory: taggedPackageDir,
OldVersion: outdatedPackage.OldVersion,
NewVersion: outdatedPackage.NewVersion,
}
}

func CleanUpdateBackups(fileSystem afero.Fs, packageBackupInfo []UpdateAttempt) error {
if len(packageBackupInfo) == 0 {
log.Debug("Not update-packages to restore.")
return nil
}

for _, info := range packageBackupInfo {

backupExists, _ := afero.Exists(fileSystem, info.BackupPackageDirectory)
//_, err1 := fileSystem.Stat(info.BackupPackageDirectory) // Checking existence
if backupExists {
err1 := fileSystem.RemoveAll(info.BackupPackageDirectory)
if err1 != nil {
log.WithFields(log.Fields{
"package": info.Package,
"problem_directory": info.BackupPackageDirectory,
}).Warn("could not delete directory during cleanup")
return err1
}
}
}

return nil
}

func RollbackUpdatePackages(fileSystem afero.Fs, packageBackupInfo []UpdateAttempt) error {

if len(packageBackupInfo) == 0 {
log.Debug("Not update-packages to restore.")
return nil
}

for _, info := range packageBackupInfo {

log.WithFields(log.Fields{
"pkg": info.Package,
"rolling back to": info.OldVersion,
"failed to update to": info.NewVersion,
}).Warn("did not update package, restoring last-installed version")

_, err1 := fileSystem.Stat(info.ActivePackageDirectory) // Checking existence
if err1 == nil {
err1 = fileSystem.RemoveAll(info.ActivePackageDirectory)
if err1 != nil {
log.WithFields(log.Fields{
"package": info.Package,
"problem_directory": info.ActivePackageDirectory,
}).Warn("could not delete directory during package rollback")
return err1
}
}

err2 := fileSystem.Rename(info.BackupPackageDirectory, info.ActivePackageDirectory)

if err2 != nil {
log.WithFields(log.Fields{
"pkg": info.Package,
}).Warn("could not rollback package -- package will need reinstallation.")
return err2
}

}

return nil
}

// UpdateAttempt ...
type UpdateAttempt struct {
Package string
ActivePackageDirectory string
BackupPackageDirectory string
OldVersion string
NewVersion string
}

// InstalledFromPkgs ...
type InstalledFromPkgs struct {
Pkgr []string `json:"pkgr"`
Expand Down
52 changes: 0 additions & 52 deletions pacman/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package pacman

import (
"path/filepath"
"testing"

"github.com/dpastoor/goutils"
Expand Down Expand Up @@ -34,57 +33,6 @@ func TestUtilsTestSuite(t *testing.T) {
suite.Run(t, new(UtilsTestSuite))
}

// Have to use an OS filesystem for this because of this issue: https://github.com/spf13/afero/issues/141
func (suite *UtilsTestSuite) TestRollbackUpdatePackages_RestoresWhenNoActiveInstallation() {

_ = suite.FileSystemOs.MkdirAll(filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges"), 0755)
_, _ = suite.FileSystemOs.Create(filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges", "DESCRIPTION"))

updateAttemptFixture := []UpdateAttempt{
UpdateAttempt{
Package: "CatsAndOranges",
BackupPackageDirectory: filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges"),
ActivePackageDirectory: filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges"),
NewVersion: "2",
OldVersion: "1",
},
}
RollbackUpdatePackages(suite.FileSystemOs, updateAttemptFixture)

suite.True(afero.DirExists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges")))
suite.True(afero.Exists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges", "DESCRIPTION")))
suite.False(afero.DirExists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges")))
suite.False(afero.Exists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library" ,"__OLD__CatsAndOranges","DESCRIPTION")))

}

// Have to use an OS filesystem for this because of this issue: https://github.com/spf13/afero/issues/141
func (suite *UtilsTestSuite) TestRollbackUpdatePackages_OverwritesFreshInstallation() {

_ = suite.FileSystemOs.MkdirAll(filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges"), 0755)
_, _ = suite.FileSystemOs.Create(filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges", "DESCRIPTION_New"))
_ = suite.FileSystemOs.MkdirAll(filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges"), 0755)
_, _ = suite.FileSystemOs.Create(filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges", "DESCRIPTION"))

updateAttemptFixture := []UpdateAttempt{
UpdateAttempt{
Package: "CatsAndOranges",
BackupPackageDirectory: filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges"),
ActivePackageDirectory: filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges"),
NewVersion: "2",
OldVersion: "1",
},
}
RollbackUpdatePackages(suite.FileSystemOs, updateAttemptFixture)

suite.True(afero.DirExists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges")))
suite.True(afero.Exists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges", "DESCRIPTION")))
suite.False(afero.Exists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "CatsAndOranges", "DESCRIPTION_New")))
suite.False(afero.DirExists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library", "__OLD__CatsAndOranges")))
suite.False(afero.Exists(suite.FileSystemOs, filepath.Join(suite.FilePrefix, "test-library" ,"__OLD__CatsAndOranges","DESCRIPTION")))

}

func (suite *UtilsTestSuite) TestScanInstalledPackage_ScansReleventFieldsForOutdatedComparison() {
_ = suite.FileSystem.MkdirAll("test-library/CatsAndOranges", 0755)
_, _ = suite.FileSystem.Create("test-library/CatsAndOranges/DESCRIPTION")
Expand Down
106 changes: 103 additions & 3 deletions rollback/operations.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package rollback

import (
"github.com/metrumresearchgroup/pkgr/pacman"
"github.com/metrumresearchgroup/pkgr/cran"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"path/filepath"
Expand Down Expand Up @@ -41,11 +41,111 @@ func RollbackPackageEnvironment(fileSystem afero.Fs, rbp RollbackPlan) error {
//Rollback updated packages -- we have to do this differently than the rest, because updated packages need to be
//restored from backups.
if len(rbp.UpdateRollbacks) > 0 {
err2 := pacman.RollbackUpdatePackages(fileSystem, rbp.UpdateRollbacks)
err2 := RollbackUpdatePackages(fileSystem, rbp.UpdateRollbacks)
if err2 != nil {
return err2
}
}

return nil
}
}

// PreparePackagesForUpdate ...
func PreparePackagesForUpdate(fileSystem afero.Fs, libraryPath string, outdatedPackages []cran.OutdatedPackage) []UpdateAttempt {
var updateAttempts []UpdateAttempt

//Tag each outdated pkg directory in library with "__OLD__"
for _, pkg := range outdatedPackages {
updateAttempts = append(updateAttempts, tagOldInstallation(fileSystem, libraryPath, pkg))
}

return updateAttempts
}

func tagOldInstallation(fileSystem afero.Fs, libraryPath string, outdatedPackage cran.OutdatedPackage) UpdateAttempt {
packageDir := filepath.Join(libraryPath, outdatedPackage.Package)
taggedPackageDir := filepath.Join(libraryPath, "__OLD__"+outdatedPackage.Package)

err := fileSystem.Rename(packageDir, taggedPackageDir)
//err := RenameDirRecursive(fileSystem, packageDir, taggedPackageDir)

if err != nil {
logrus.WithField("package dir", packageDir).Warn("error when backing up outdated package")
logrus.Error(err)
}

return UpdateAttempt{
Package: outdatedPackage.Package,
ActivePackageDirectory: packageDir,
BackupPackageDirectory: taggedPackageDir,
OldVersion: outdatedPackage.OldVersion,
NewVersion: outdatedPackage.NewVersion,
}
}

func CleanUpdateBackups(fileSystem afero.Fs, packageBackupInfo []UpdateAttempt) error {
if len(packageBackupInfo) == 0 {
logrus.Debug("Not update-packages to restore.")
return nil
}

for _, info := range packageBackupInfo {

backupExists, _ := afero.Exists(fileSystem, info.BackupPackageDirectory)
//_, err1 := fileSystem.Stat(info.BackupPackageDirectory) // Checking existence
if backupExists {
err1 := fileSystem.RemoveAll(info.BackupPackageDirectory)
if err1 != nil {
logrus.WithFields(logrus.Fields{
"package": info.Package,
"problem_directory": info.BackupPackageDirectory,
}).Warn("could not delete directory during cleanup")
return err1
}
}
}

return nil
}

func RollbackUpdatePackages(fileSystem afero.Fs, packageBackupInfo []UpdateAttempt) error {

if len(packageBackupInfo) == 0 {
logrus.Debug("Not update-packages to restore.")
return nil
}

for _, info := range packageBackupInfo {

logrus.WithFields(logrus.Fields{
"pkg": info.Package,
"rolling back to": info.OldVersion,
"failed to update to": info.NewVersion,
}).Warn("did not update package, restoring last-installed version")

_, err1 := fileSystem.Stat(info.ActivePackageDirectory) // Checking existence
if err1 == nil {
err1 = fileSystem.RemoveAll(info.ActivePackageDirectory)
if err1 != nil {
logrus.WithFields(logrus.Fields{
"package": info.Package,
"problem_directory": info.ActivePackageDirectory,
}).Warn("could not delete directory during package rollback")
return err1
}
}

err2 := fileSystem.Rename(info.BackupPackageDirectory, info.ActivePackageDirectory)

if err2 != nil {
logrus.WithFields(logrus.Fields{
"pkg": info.Package,
}).Warn("could not rollback package -- package will need reinstallation.")
return err2
}

}

return nil
}

Loading

0 comments on commit ae2d575

Please sign in to comment.