A cross-vault secret mv that cannot remove the source fails (closes #159)
check / check (push) Waiting to run
check / check (push) Waiting to run
When `secret mv` copied a secret into another vault but could not remove the source, it printed a warning and the usual "Moved" line and exited 0. It now fails. If the source secret's directory is still in place, the error says the copy holds all versions and the source remains. If the source left its vault and only the temporary directory it was moved into could not be deleted, the error says the secret is moved and names that directory, which the error of RemoveDirAtomic now does. The tests are in package cli and call moveSecret, because the state directory lock refuses the wrapped filesystems that make the removal fail. Model: opus-5-5
This commit is contained in:
+31
-6
@@ -1097,12 +1097,9 @@ func (cli *Instance) moveSecretCrossVault(
|
||||
// Delete source secret
|
||||
err = secret.RemoveDirAtomic(cli.fs, srcSecretDir)
|
||||
if err != nil {
|
||||
// Copy succeeded but delete failed - warn but don't fail
|
||||
cmd.Printf("Warning: copied secret but failed to remove source: %v\n", err)
|
||||
cmd.Printf("Moved secret '%s:%s' to '%s:%s' (%d version(s))\n",
|
||||
srcVault.Name, srcSecretName, destVault.Name, destSecretName, versionCount)
|
||||
|
||||
return nil
|
||||
return cli.sourceRemovalError(err, srcSecretDir,
|
||||
srcVault.Name+":"+srcSecretName, destVault.Name+":"+destSecretName,
|
||||
versionCount)
|
||||
}
|
||||
|
||||
cmd.Printf("Moved secret '%s:%s' to '%s:%s' (%d version(s))\n",
|
||||
@@ -1110,3 +1107,31 @@ func (cli *Instance) moveSecretCrossVault(
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sourceRemovalError returns the error of a move between vaults that
|
||||
// copied all versionCount versions of the secret srcName to destName, and
|
||||
// then failed with removeErr to remove srcSecretDir, the directory of
|
||||
// srcName. The removal can fail after the source has left its vault, when
|
||||
// only the temporary directory it was moved into cannot be deleted, so the
|
||||
// error says the source remains only if srcSecretDir is still there.
|
||||
func (cli *Instance) sourceRemovalError(
|
||||
removeErr error, srcSecretDir, srcName, destName string, versionCount int,
|
||||
) error {
|
||||
remains, err := afero.DirExists(cli.fs, srcSecretDir)
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return fmt.Errorf("secret '%s' is copied with all %d version(s) "+
|
||||
"to '%s', but removing the source failed and whether it "+
|
||||
"remains is unknown: %w",
|
||||
srcName, versionCount, destName, errors.Join(removeErr, err))
|
||||
case remains:
|
||||
return fmt.Errorf("secret '%s' is copied with all %d version(s) "+
|
||||
"to '%s', but the source remains: %w",
|
||||
srcName, versionCount, destName, removeErr)
|
||||
default:
|
||||
return fmt.Errorf("secret '%s' is moved with all %d version(s) "+
|
||||
"to '%s', but its old files could not all be deleted: %w",
|
||||
srcName, versionCount, destName, removeErr)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user