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