2025-10-21 17:20:20 -04:00
// Copyright IBM Corp. 2016, 2025
2023-08-10 21:14:03 -04:00
// SPDX-License-Identifier: BUSL-1.1
2023-03-15 12:00:52 -04:00
2017-09-07 22:01:31 -04:00
package command
import (
"fmt"
"strings"
2022-02-18 11:50:05 -05:00
"time"
2017-09-07 22:01:31 -04:00
2023-12-04 14:05:02 -05:00
"github.com/hashicorp/cli"
2017-09-07 22:01:31 -04:00
"github.com/posener/complete"
)
2021-04-08 12:43:39 -04:00
var (
_ cli . Command = ( * SecretsMoveCommand ) ( nil )
_ cli . CommandAutocomplete = ( * SecretsMoveCommand ) ( nil )
)
2017-09-07 22:01:31 -04:00
2022-02-18 11:50:05 -05:00
const (
MountMigrationStatusSuccess = "success"
MountMigrationStatusFailure = "failure"
)
2017-09-07 22:01:31 -04:00
type SecretsMoveCommand struct {
* BaseCommand
}
func ( c * SecretsMoveCommand ) Synopsis ( ) string {
return "Move a secrets engine to a new path"
}
func ( c * SecretsMoveCommand ) Help ( ) string {
helpText := `
Usage : vault secrets move [ options ] SOURCE DESTINATION
Moves an existing secrets engine to a new path . Any leases from the old
secrets engine are revoked , but all configuration associated with the engine
2022-02-18 11:50:05 -05:00
is preserved . It initiates the migration and intermittently polls its status ,
exiting if a final state is reached .
2017-09-07 22:01:31 -04:00
2022-02-17 15:17:59 -05:00
This command works within or across namespaces , both source and destination paths
can be prefixed with a namespace heirarchy relative to the current namespace .
2018-11-07 11:07:19 -05:00
2022-02-18 11:50:05 -05:00
WARNING ! Moving a secrets engine will revoke any leases from the
2017-09-07 22:01:31 -04:00
old engine .
2022-02-18 11:50:05 -05:00
Move the secrets engine at secret / to generic / :
2017-09-07 22:01:31 -04:00
$ vault secrets move secret / generic /
2022-02-18 11:50:05 -05:00
Move the secrets engine at ns1 / secret / across namespaces to ns2 / generic / ,
2022-02-17 15:17:59 -05:00
where ns1 and ns2 are child namespaces of the current namespace :
$ vault secrets move ns1 / secret / ns2 / generic /
2017-09-07 22:01:31 -04:00
` + c . Flags ( ) . Help ( )
return strings . TrimSpace ( helpText )
}
func ( c * SecretsMoveCommand ) Flags ( ) * FlagSets {
return c . flagSet ( FlagSetHTTP )
}
func ( c * SecretsMoveCommand ) AutocompleteArgs ( ) complete . Predictor {
return c . PredictVaultMounts ( )
}
func ( c * SecretsMoveCommand ) AutocompleteFlags ( ) complete . Flags {
return c . Flags ( ) . Completions ( )
}
func ( c * SecretsMoveCommand ) Run ( args [ ] string ) int {
f := c . Flags ( )
if err := f . Parse ( args ) ; err != nil {
c . UI . Error ( err . Error ( ) )
return 1
}
args = f . Args ( )
switch {
case len ( args ) < 2 :
c . UI . Error ( fmt . Sprintf ( "Not enough arguments (expected 2, got %d)" , len ( args ) ) )
return 1
case len ( args ) > 2 :
c . UI . Error ( fmt . Sprintf ( "Too many arguments (expected 2, got %d)" , len ( args ) ) )
return 1
}
// Grab the source and destination
source := ensureTrailingSlash ( args [ 0 ] )
destination := ensureTrailingSlash ( args [ 1 ] )
client , err := c . Client ( )
if err != nil {
c . UI . Error ( err . Error ( ) )
return 2
}
2022-02-17 15:17:59 -05:00
remountResp , err := client . Sys ( ) . StartRemount ( source , destination )
if err != nil {
2017-09-07 22:01:31 -04:00
c . UI . Error ( fmt . Sprintf ( "Error moving secrets engine %s to %s: %s" , source , destination , err ) )
return 2
}
2022-02-18 11:50:05 -05:00
c . UI . Output ( fmt . Sprintf ( "Started moving secrets engine %s to %s, with migration ID %s" , source , destination , remountResp . MigrationID ) )
// Poll the status endpoint with the returned migration ID
// Exit if a terminal status is reached, else wait and retry
for {
remountStatusResp , err := client . Sys ( ) . RemountStatus ( remountResp . MigrationID )
if err != nil {
c . UI . Error ( fmt . Sprintf ( "Error checking migration status of secrets engine %s to %s: %s" , source , destination , err ) )
return 2
}
if remountStatusResp . MigrationInfo . MigrationStatus == MountMigrationStatusSuccess {
c . UI . Output ( fmt . Sprintf ( "Success! Finished moving secrets engine %s to %s, with migration ID %s" , source , destination , remountResp . MigrationID ) )
return 0
}
if remountStatusResp . MigrationInfo . MigrationStatus == MountMigrationStatusFailure {
c . UI . Error ( fmt . Sprintf ( "Failure! Error encountered moving secrets engine %s to %s, with migration ID %s" , source , destination , remountResp . MigrationID ) )
return 0
}
c . UI . Output ( fmt . Sprintf ( "Waiting for terminal status in migration of secrets engine %s to %s, with migration ID %s" , source , destination , remountResp . MigrationID ) )
time . Sleep ( 10 * time . Second )
}
2017-09-07 22:01:31 -04:00
return 0
}