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