github.com/kwoods/terraform@v0.6.11-0.20160809170336-13497db7138e/command/state_mv.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/terraform/terraform" 8 "github.com/mitchellh/cli" 9 ) 10 11 // StateMvCommand is a Command implementation that shows a single resource. 12 type StateMvCommand struct { 13 Meta 14 StateMeta 15 } 16 17 func (c *StateMvCommand) Run(args []string) int { 18 args = c.Meta.process(args, true) 19 20 // We create two metas to track the two states 21 var meta1, meta2 Meta 22 cmdFlags := c.Meta.flagSet("state mv") 23 cmdFlags.StringVar(&meta1.stateOutPath, "backup", "", "backup") 24 cmdFlags.StringVar(&meta1.statePath, "state", DefaultStateFilename, "path") 25 cmdFlags.StringVar(&meta2.stateOutPath, "backup-out", "", "backup") 26 cmdFlags.StringVar(&meta2.statePath, "state-out", "", "path") 27 if err := cmdFlags.Parse(args); err != nil { 28 return cli.RunResultHelp 29 } 30 args = cmdFlags.Args() 31 if len(args) != 2 { 32 c.Ui.Error("Exactly two arguments expected.\n") 33 return cli.RunResultHelp 34 } 35 36 // Copy the `-state` flag for output if we weren't given a custom one 37 if meta2.statePath == "" { 38 meta2.statePath = meta1.statePath 39 } 40 41 // Read the from state 42 stateFrom, err := c.StateMeta.State(&meta1) 43 if err != nil { 44 c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) 45 return cli.RunResultHelp 46 } 47 48 stateFromReal := stateFrom.State() 49 if stateFromReal == nil { 50 c.Ui.Error(fmt.Sprintf(errStateNotFound)) 51 return 1 52 } 53 54 // Read the destination state 55 stateTo := stateFrom 56 stateToReal := stateFromReal 57 if meta2.statePath != meta1.statePath { 58 stateTo, err = c.StateMeta.State(&meta2) 59 if err != nil { 60 c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) 61 return cli.RunResultHelp 62 } 63 64 stateToReal = stateTo.State() 65 if stateToReal == nil { 66 stateToReal = terraform.NewState() 67 } 68 } 69 70 // Filter what we're moving 71 filter := &terraform.StateFilter{State: stateFromReal} 72 results, err := filter.Filter(args[0]) 73 if err != nil { 74 c.Ui.Error(fmt.Sprintf(errStateMv, err)) 75 return cli.RunResultHelp 76 } 77 if len(results) == 0 { 78 c.Ui.Output(fmt.Sprintf("Item to move doesn't exist: %s", args[0])) 79 return 1 80 } 81 82 // Do the actual move 83 if err := stateFromReal.Remove(args[0]); err != nil { 84 c.Ui.Error(fmt.Sprintf(errStateMv, err)) 85 return 1 86 } 87 88 if err := stateToReal.Add(args[0], args[1], results[0].Value); err != nil { 89 c.Ui.Error(fmt.Sprintf(errStateMv, err)) 90 return 1 91 } 92 93 // Write the new state 94 if err := stateTo.WriteState(stateToReal); err != nil { 95 c.Ui.Error(fmt.Sprintf(errStateMvPersist, err)) 96 return 1 97 } 98 99 if err := stateTo.PersistState(); err != nil { 100 c.Ui.Error(fmt.Sprintf(errStateMvPersist, err)) 101 return 1 102 } 103 104 // Write the old state if it is different 105 if stateTo != stateFrom { 106 if err := stateFrom.WriteState(stateFromReal); err != nil { 107 c.Ui.Error(fmt.Sprintf(errStateMvPersist, err)) 108 return 1 109 } 110 111 if err := stateFrom.PersistState(); err != nil { 112 c.Ui.Error(fmt.Sprintf(errStateMvPersist, err)) 113 return 1 114 } 115 } 116 117 c.Ui.Output(fmt.Sprintf( 118 "Moved %s to %s", args[0], args[1])) 119 return 0 120 } 121 122 func (c *StateMvCommand) Help() string { 123 helpText := ` 124 Usage: terraform state mv [options] ADDRESS ADDRESS 125 126 Move an item in the state to another location or to a completely different 127 state file. 128 129 This command is useful for module refactors (moving items into a module), 130 configuration refactors (moving items to a completely different or new 131 state file), or generally renaming of resources. 132 133 This command creates a timestamped backup of the state on every invocation. 134 This can't be disabled. Due to the destructive nature of this command, 135 the backup is ensured by Terraform for safety reasons. 136 137 If you're moving from one state file to a different state file, a backup 138 will be created for each state file. 139 140 Options: 141 142 -backup=PATH Path where Terraform should write the backup for the original 143 state. This can't be disabled. If not set, Terraform 144 will write it to the same path as the statefile with 145 a backup extension. 146 147 -backup-out=PATH Path where Terraform should write the backup for the destination 148 state. This can't be disabled. If not set, Terraform 149 will write it to the same path as the destination state 150 file with a backup extension. This only needs 151 to be specified if -state-out is set to a different path 152 than -state. 153 154 -state=PATH Path to a Terraform state file to use to look 155 up Terraform-managed resources. By default it will 156 use the state "terraform.tfstate" if it exists. 157 158 -state-out=PATH Path to the destination state file to move the item 159 to. This defaults to the same statefile. This will 160 overwrite the destination state file. 161 162 ` 163 return strings.TrimSpace(helpText) 164 } 165 166 func (c *StateMvCommand) Synopsis() string { 167 return "Move an item in the state" 168 } 169 170 const errStateMv = `Error moving state: %[1]s 171 172 Please ensure your addresses and state paths are valid. No 173 state was persisted. Your existing states are untouched.` 174 175 const errStateMvPersist = `Error saving the state: %s 176 177 The state wasn't saved properly. If the error happening after a partial 178 write occurred, a backup file will have been created. Otherwise, the state 179 is in the same state it was when the operation started.`