github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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.`