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