Skip to content

Commit 80bc99c

Browse files
mergify[bot]Kaan Yalti
andauthored
Enhancement/5235 correctly wrap errors from copyActionDir and copyRunDirectory (#9349) (#9935)
* enhancement(5235): added copyActionStore package var * enhancement(5235): added copyRunDirectory package var * enhancement(5235): wrapping errors in copyRunDirectory enhancement(5235): wrapping mkdriall error in copyrundir function * enhancement(5235): added package var to abstract third party package function copy.Copy * enhancement(5235): using copyActionStoreFunc and copyRunDirectoryFunc * enhancement(5235): wrapping errors in upgrade function * enhancement(5235): added mkdirall wrapper in copyRunDir enhancement(5235): added common package import * enhancement(5235): using writefile wrapper in copyActionStore * enhancement(5235): using readFile wrapper in copyActionStore * enhancement(5235): added copyActionStore and copyRunDirectory tests in upgrade_test enhancement(5235): updated the copyRunDirectory test to mock stdlib functions * enhancement(5235): added TestUpgradeDirectoryCopyErrors test in upgrade_test enhancement(5235): added test imports in upgrade_test * enhancement(5235): added comment in step_unpack * enhancement(5235): added types in upgrade.go to abstract away functions for testability. abstracted copyActionStore and copyRunDirectory. Updated tests * enhancement(5235): updated upgrade tests, added tests cases for copyActionStore and copyRunDir error handling, removed unnecessary test, refactored copyActionStore and copyRunDirectory tests * enhancement(5235): moved where mkdriAllFunc type is declared * enhancement(5235): remove unnecessary change * enhancement(5235): added action store path to error message * enhancement(5235): remove unused commented code * enhancement(5235): fix typo in test log name (cherry picked from commit 96e0476) Co-authored-by: Kaan Yalti <[email protected]>
1 parent be07917 commit 80bc99c

File tree

3 files changed

+306
-42
lines changed

3 files changed

+306
-42
lines changed

internal/pkg/agent/application/upgrade/step_unpack.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ type UnpackResult struct {
3636
}
3737

3838
type copyFunc func(dst io.Writer, src io.Reader) (written int64, err error)
39-
type mkdirAllFunc func(name string, perm fs.FileMode) error
4039
type openFileFunc func(name string, flag int, perm fs.FileMode) (*os.File, error)
4140
type unarchiveFunc func(log *logger.Logger, archivePath, dataDir string, flavor string, copy copyFunc, mkdirAll mkdirAllFunc, openFile openFileFunc) (UnpackResult, error)
4241

internal/pkg/agent/application/upgrade/upgrade.go

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ type unpackHandler interface {
7070
getPackageMetadata(archivePath string) (packageMetadata, error)
7171
}
7272

73+
// Types used to abstract copyActionStore, copyRunDirectory and github.com/otiai10/copy.Copy
74+
type copyActionStoreFunc func(log *logger.Logger, newHome string) error
75+
type copyRunDirectoryFunc func(log *logger.Logger, oldRunPath, newRunPath string) error
76+
type fileDirCopyFunc func(from, to string, opts ...copy.Options) error
77+
78+
// Types used to abstract stdlib functions
79+
type mkdirAllFunc func(name string, perm fs.FileMode) error
80+
type readFileFunc func(name string) ([]byte, error)
81+
type writeFileFunc func(name string, data []byte, perm fs.FileMode) error
82+
7383
// Upgrader performs an upgrade
7484
type Upgrader struct {
7585
log *logger.Logger
@@ -84,6 +94,8 @@ type Upgrader struct {
8494
unpacker unpackHandler
8595
isDiskSpaceErrorFunc func(err error) bool
8696
extractAgentVersion func(metadata packageMetadata, upgradeVersion string) agentVersion
97+
copyActionStore copyActionStoreFunc
98+
copyRunDirectory copyRunDirectoryFunc
8799
}
88100

89101
// IsUpgradeable when agent is installed and running as a service or flag was provided.
@@ -105,6 +117,8 @@ func NewUpgrader(log *logger.Logger, settings *artifact.Config, agentInfo info.A
105117
unpacker: newUnpacker(log),
106118
isDiskSpaceErrorFunc: upgradeErrors.IsDiskSpaceError,
107119
extractAgentVersion: extractAgentVersion,
120+
copyActionStore: copyActionStoreProvider(os.ReadFile, os.WriteFile),
121+
copyRunDirectory: copyRunDirectoryProvider(os.MkdirAll, copy.Copy),
108122
}, nil
109123
}
110124

@@ -310,15 +324,15 @@ func (u *Upgrader) Upgrade(ctx context.Context, version string, sourceURI string
310324

311325
newHome := filepath.Join(paths.Top(), unpackRes.VersionedHome)
312326

313-
if err := copyActionStore(u.log, newHome); err != nil {
314-
return nil, errors.New(err, "failed to copy action store")
327+
if err := u.copyActionStore(u.log, newHome); err != nil {
328+
return nil, fmt.Errorf("failed to copy action store: %w", err)
315329
}
316330

317331
newRunPath := filepath.Join(newHome, "run")
318332
oldRunPath := filepath.Join(paths.Run())
319333

320-
if err := copyRunDirectory(u.log, oldRunPath, newRunPath); err != nil {
321-
return nil, errors.New(err, "failed to copy run directory")
334+
if err := u.copyRunDirectory(u.log, oldRunPath, newRunPath); err != nil {
335+
return nil, fmt.Errorf("failed to copy run directory: %w", err)
322336
}
323337

324338
det.SetState(details.StateReplacing)
@@ -537,49 +551,55 @@ func rollbackInstall(ctx context.Context, log *logger.Logger, topDirPath, versio
537551
return nil
538552
}
539553

540-
func copyActionStore(log *logger.Logger, newHome string) error {
541-
// copies legacy action_store.yml, state.yml and state.enc encrypted file if exists
542-
storePaths := []string{paths.AgentActionStoreFile(), paths.AgentStateStoreYmlFile(), paths.AgentStateStoreFile()}
543-
log.Infow("Copying action store", "new_home_path", newHome)
554+
func copyActionStoreProvider(readFile readFileFunc, writeFile writeFileFunc) copyActionStoreFunc {
555+
return func(log *logger.Logger, newHome string) error {
556+
// copies legacy action_store.yml, state.yml and state.enc encrypted file if exists
557+
storePaths := []string{paths.AgentActionStoreFile(), paths.AgentStateStoreYmlFile(), paths.AgentStateStoreFile()}
558+
log.Infow("Copying action store", "new_home_path", newHome)
559+
560+
for _, currentActionStorePath := range storePaths {
561+
newActionStorePath := filepath.Join(newHome, filepath.Base(currentActionStorePath))
562+
log.Infow("Copying action store path", "from", currentActionStorePath, "to", newActionStorePath)
563+
// using readfile instead of os.ReadFile for testability
564+
currentActionStore, err := readFile(currentActionStorePath)
565+
if os.IsNotExist(err) {
566+
// nothing to copy
567+
continue
568+
}
569+
if err != nil {
570+
return err
571+
}
544572

545-
for _, currentActionStorePath := range storePaths {
546-
newActionStorePath := filepath.Join(newHome, filepath.Base(currentActionStorePath))
547-
log.Infow("Copying action store path", "from", currentActionStorePath, "to", newActionStorePath)
548-
currentActionStore, err := os.ReadFile(currentActionStorePath)
549-
if os.IsNotExist(err) {
550-
// nothing to copy
551-
continue
552-
}
553-
if err != nil {
554-
return err
573+
// using writeFile instead of os.WriteFile for testability
574+
if err := writeFile(newActionStorePath, currentActionStore, 0o600); err != nil {
575+
return fmt.Errorf("failed to write action store at %q: %w", newActionStorePath, err)
576+
}
555577
}
556578

557-
if err := os.WriteFile(newActionStorePath, currentActionStore, 0o600); err != nil {
558-
return err
559-
}
579+
return nil
560580
}
561-
562-
return nil
563581
}
564582

565-
func copyRunDirectory(log *logger.Logger, oldRunPath, newRunPath string) error {
566-
log.Infow("Copying run directory", "new_run_path", newRunPath, "old_run_path", oldRunPath)
583+
func copyRunDirectoryProvider(mkdirAll mkdirAllFunc, fileDirCopy fileDirCopyFunc) copyRunDirectoryFunc {
584+
return func(log *logger.Logger, oldRunPath, newRunPath string) error {
585+
log.Infow("Copying run directory", "new_run_path", newRunPath, "old_run_path", oldRunPath)
567586

568-
if err := os.MkdirAll(newRunPath, runDirMod); err != nil {
569-
return errors.New(err, "failed to create run directory")
570-
}
587+
if err := mkdirAll(newRunPath, runDirMod); err != nil {
588+
return fmt.Errorf("failed to create run directory: %w", err)
589+
}
590+
591+
err := copyDir(log, oldRunPath, newRunPath, true, fileDirCopy)
592+
if os.IsNotExist(err) {
593+
// nothing to copy, operation ok
594+
log.Infow("Run directory not present", "old_run_path", oldRunPath)
595+
return nil
596+
}
597+
if err != nil {
598+
return fmt.Errorf("failed to copy %q to %q: %w", oldRunPath, newRunPath, err)
599+
}
571600

572-
err := copyDir(log, oldRunPath, newRunPath, true)
573-
if os.IsNotExist(err) {
574-
// nothing to copy, operation ok
575-
log.Infow("Run directory not present", "old_run_path", oldRunPath)
576601
return nil
577602
}
578-
if err != nil {
579-
return errors.New(err, "failed to copy %q to %q", oldRunPath, newRunPath)
580-
}
581-
582-
return nil
583603
}
584604

585605
// shutdownCallback returns a callback function to be executing during shutdown once all processes are closed.
@@ -607,7 +627,7 @@ func shutdownCallback(l *logger.Logger, homePath, prevVersion, newVersion, newHo
607627
newRelPath = strings.ReplaceAll(newRelPath, oldHome, newHome)
608628
newDir := filepath.Join(newHome, newRelPath)
609629
l.Debugf("copying %q -> %q", processDir, newDir)
610-
if err := copyDir(l, processDir, newDir, true); err != nil {
630+
if err := copyDir(l, processDir, newDir, true, copy.Copy); err != nil {
611631
return err
612632
}
613633
}
@@ -653,7 +673,7 @@ func readDirs(dir string) ([]string, error) {
653673
return dirs, nil
654674
}
655675

656-
func copyDir(l *logger.Logger, from, to string, ignoreErrs bool) error {
676+
func copyDir(l *logger.Logger, from, to string, ignoreErrs bool, fileDirCopy fileDirCopyFunc) error {
657677
var onErr func(src, dst string, err error) error
658678

659679
if ignoreErrs {
@@ -679,7 +699,7 @@ func copyDir(l *logger.Logger, from, to string, ignoreErrs bool) error {
679699
copyConcurrency = runtime.NumCPU() * 4
680700
}
681701

682-
return copy.Copy(from, to, copy.Options{
702+
return fileDirCopy(from, to, copy.Options{
683703
OnSymlink: func(_ string) copy.SymlinkAction {
684704
return copy.Shallow
685705
},

0 commit comments

Comments
 (0)