github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/command/state_push.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/terraform"
     9  	"github.com/mitchellh/cli"
    10  )
    11  
    12  // StatePushCommand is a Command implementation that shows a single resource.
    13  type StatePushCommand struct {
    14  	Meta
    15  	StateMeta
    16  }
    17  
    18  func (c *StatePushCommand) Run(args []string) int {
    19  	args = c.Meta.process(args, true)
    20  
    21  	var flagForce bool
    22  	cmdFlags := c.Meta.flagSet("state push")
    23  	cmdFlags.BoolVar(&flagForce, "force", false, "")
    24  	if err := cmdFlags.Parse(args); err != nil {
    25  		return cli.RunResultHelp
    26  	}
    27  	args = cmdFlags.Args()
    28  
    29  	if len(args) != 1 {
    30  		c.Ui.Error("Exactly one argument expected: path to state to push")
    31  		return 1
    32  	}
    33  
    34  	// Read the state
    35  	f, err := os.Open(args[0])
    36  	if err != nil {
    37  		c.Ui.Error(err.Error())
    38  		return 1
    39  	}
    40  	sourceState, err := terraform.ReadState(f)
    41  	f.Close()
    42  	if err != nil {
    43  		c.Ui.Error(fmt.Sprintf("Error reading source state %q: %s", args[0], err))
    44  		return 1
    45  	}
    46  
    47  	// Load the backend
    48  	b, err := c.Backend(nil)
    49  	if err != nil {
    50  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    51  		return 1
    52  	}
    53  
    54  	// Get the state
    55  	state, err := b.State()
    56  	if err != nil {
    57  		c.Ui.Error(fmt.Sprintf("Failed to load destination state: %s", err))
    58  		return 1
    59  	}
    60  	if err := state.RefreshState(); err != nil {
    61  		c.Ui.Error(fmt.Sprintf("Failed to load destination state: %s", err))
    62  		return 1
    63  	}
    64  	dstState := state.State()
    65  
    66  	// If we're not forcing, then perform safety checks
    67  	if !flagForce && !dstState.Empty() {
    68  		if !dstState.SameLineage(sourceState) {
    69  			c.Ui.Error(strings.TrimSpace(errStatePushLineage))
    70  			return 1
    71  		}
    72  
    73  		age, err := dstState.CompareAges(sourceState)
    74  		if err != nil {
    75  			c.Ui.Error(err.Error())
    76  			return 1
    77  		}
    78  		if age == terraform.StateAgeReceiverNewer {
    79  			c.Ui.Error(strings.TrimSpace(errStatePushSerialNewer))
    80  			return 1
    81  		}
    82  	}
    83  
    84  	// Overwrite it
    85  	if err := state.WriteState(sourceState); err != nil {
    86  		c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
    87  		return 1
    88  	}
    89  	if err := state.PersistState(); err != nil {
    90  		c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
    91  		return 1
    92  	}
    93  
    94  	return 0
    95  }
    96  
    97  func (c *StatePushCommand) Help() string {
    98  	helpText := `
    99  Usage: terraform state push [options] PATH
   100  
   101    Update remote state from a local state file at PATH.
   102  
   103    This command "pushes" a local state and overwrites remote state
   104    with a local state file. The command will protect you against writing
   105    an older serial or a different state file lineage unless you specify the
   106    "-force" flag.
   107  
   108    This command works with local state (it will overwrite the local
   109    state), but is less useful for this use case.
   110  
   111  Options:
   112  
   113    -force              Write the state even if lineages don't match or the
   114                        remote serial is higher.
   115  
   116  `
   117  	return strings.TrimSpace(helpText)
   118  }
   119  
   120  func (c *StatePushCommand) Synopsis() string {
   121  	return "Update remote state from a local state file"
   122  }
   123  
   124  const errStatePushLineage = `
   125  The lineages do not match! The state will not be pushed.
   126  
   127  The "lineage" is a unique identifier given to a state on creation. It helps
   128  protect Terraform from overwriting a seemingly unrelated state file since it
   129  represents potentially losing real state.
   130  
   131  Please verify you're pushing the correct state. If you're sure you are, you
   132  can force the behavior with the "-force" flag.
   133  `
   134  
   135  const errStatePushSerialNewer = `
   136  The destination state has a higher serial number! The state will not be pushed.
   137  
   138  A higher serial could indicate that there is data in the destination state
   139  that was not present when the source state was created. As a protection measure,
   140  Terraform will not automatically overwrite this state.
   141  
   142  Please verify you're pushing the correct state. If you're sure you are, you
   143  can force the behavior with the "-force" flag.
   144  `