github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/state_replace_provider.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/terraform/internal/addrs" 8 "github.com/hashicorp/terraform/internal/command/arguments" 9 "github.com/hashicorp/terraform/internal/command/clistate" 10 "github.com/hashicorp/terraform/internal/command/views" 11 "github.com/hashicorp/terraform/internal/states" 12 "github.com/hashicorp/terraform/internal/terraform" 13 "github.com/hashicorp/terraform/internal/tfdiags" 14 "github.com/mitchellh/cli" 15 ) 16 17 // StateReplaceProviderCommand is a Command implementation that allows users 18 // to change the provider associated with existing resources. This is only 19 // likely to be useful if a provider is forked or changes its fully-qualified 20 // name. 21 22 type StateReplaceProviderCommand struct { 23 StateMeta 24 } 25 26 func (c *StateReplaceProviderCommand) Run(args []string) int { 27 args = c.Meta.process(args) 28 29 var autoApprove bool 30 cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state replace-provider") 31 cmdFlags.BoolVar(&autoApprove, "auto-approve", false, "skip interactive approval of replacements") 32 cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup") 33 cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock states") 34 cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") 35 cmdFlags.StringVar(&c.statePath, "state", "", "path") 36 if err := cmdFlags.Parse(args); err != nil { 37 c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) 38 return cli.RunResultHelp 39 } 40 args = cmdFlags.Args() 41 if len(args) != 2 { 42 c.Ui.Error("Exactly two arguments expected.\n") 43 return cli.RunResultHelp 44 } 45 46 if diags := c.Meta.checkRequiredVersion(); diags != nil { 47 c.showDiagnostics(diags) 48 return 1 49 } 50 51 var diags tfdiags.Diagnostics 52 53 // Parse from/to arguments into providers 54 from, fromDiags := addrs.ParseProviderSourceString(args[0]) 55 if fromDiags.HasErrors() { 56 diags = diags.Append(tfdiags.Sourceless( 57 tfdiags.Error, 58 fmt.Sprintf(`Invalid "from" provider %q`, args[0]), 59 fromDiags.Err().Error(), 60 )) 61 } 62 to, toDiags := addrs.ParseProviderSourceString(args[1]) 63 if toDiags.HasErrors() { 64 diags = diags.Append(tfdiags.Sourceless( 65 tfdiags.Error, 66 fmt.Sprintf(`Invalid "to" provider %q`, args[1]), 67 toDiags.Err().Error(), 68 )) 69 } 70 if diags.HasErrors() { 71 c.showDiagnostics(diags) 72 return 1 73 } 74 75 // Initialize the state manager as configured 76 stateMgr, err := c.State() 77 if err != nil { 78 c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) 79 return 1 80 } 81 82 // Acquire lock if requested 83 if c.stateLock { 84 stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) 85 if diags := stateLocker.Lock(stateMgr, "state-replace-provider"); diags.HasErrors() { 86 c.showDiagnostics(diags) 87 return 1 88 } 89 defer func() { 90 if diags := stateLocker.Unlock(); diags.HasErrors() { 91 c.showDiagnostics(diags) 92 } 93 }() 94 } 95 96 // Refresh and load state 97 if err := stateMgr.RefreshState(); err != nil { 98 c.Ui.Error(fmt.Sprintf("Failed to refresh source state: %s", err)) 99 return 1 100 } 101 102 state := stateMgr.State() 103 if state == nil { 104 c.Ui.Error(errStateNotFound) 105 return 1 106 } 107 108 // Fetch all resources from the state 109 resources, diags := c.lookupAllResources(state) 110 if diags.HasErrors() { 111 c.showDiagnostics(diags) 112 return 1 113 } 114 115 var willReplace []*states.Resource 116 117 // Update all matching resources with new provider 118 for _, resource := range resources { 119 if resource.ProviderConfig.Provider.Equals(from) { 120 willReplace = append(willReplace, resource) 121 } 122 } 123 c.showDiagnostics(diags) 124 125 if len(willReplace) == 0 { 126 c.Ui.Output("No matching resources found.") 127 return 0 128 } 129 130 // Explain the changes 131 colorize := c.Colorize() 132 c.Ui.Output("Terraform will perform the following actions:\n") 133 c.Ui.Output(colorize.Color(" [yellow]~[reset] Updating provider:")) 134 c.Ui.Output(colorize.Color(fmt.Sprintf(" [red]-[reset] %s", from))) 135 c.Ui.Output(colorize.Color(fmt.Sprintf(" [green]+[reset] %s\n", to))) 136 137 c.Ui.Output(colorize.Color(fmt.Sprintf("[bold]Changing[reset] %d resources:\n", len(willReplace)))) 138 for _, resource := range willReplace { 139 c.Ui.Output(colorize.Color(fmt.Sprintf(" %s", resource.Addr))) 140 } 141 142 // Confirm 143 if !autoApprove { 144 c.Ui.Output(colorize.Color( 145 "\n[bold]Do you want to make these changes?[reset]\n" + 146 "Only 'yes' will be accepted to continue.\n", 147 )) 148 v, err := c.Ui.Ask("Enter a value:") 149 if err != nil { 150 c.Ui.Error(fmt.Sprintf("Error asking for approval: %s", err)) 151 return 1 152 } 153 if v != "yes" { 154 c.Ui.Output("Cancelled replacing providers.") 155 return 0 156 } 157 } 158 159 // Update the provider for each resource 160 for _, resource := range willReplace { 161 resource.ProviderConfig.Provider = to 162 } 163 164 b, backendDiags := c.Backend(nil) 165 diags = diags.Append(backendDiags) 166 if backendDiags.HasErrors() { 167 c.showDiagnostics(diags) 168 return 1 169 } 170 171 // Get schemas, if possible, before writing state 172 var schemas *terraform.Schemas 173 if isCloudMode(b) { 174 var schemaDiags tfdiags.Diagnostics 175 schemas, schemaDiags = c.MaybeGetSchemas(state, nil) 176 diags = diags.Append(schemaDiags) 177 } 178 179 // Write the updated state 180 if err := stateMgr.WriteState(state); err != nil { 181 c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) 182 return 1 183 } 184 if err := stateMgr.PersistState(schemas); err != nil { 185 c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) 186 return 1 187 } 188 189 c.showDiagnostics(diags) 190 c.Ui.Output(fmt.Sprintf("\nSuccessfully replaced provider for %d resources.", len(willReplace))) 191 return 0 192 } 193 194 func (c *StateReplaceProviderCommand) Help() string { 195 helpText := ` 196 Usage: terraform [global options] state replace-provider [options] FROM_PROVIDER_FQN TO_PROVIDER_FQN 197 198 Replace provider for resources in the Terraform state. 199 200 Options: 201 202 -auto-approve Skip interactive approval. 203 204 -lock=false Don't hold a state lock during the operation. This is 205 dangerous if others might concurrently run commands 206 against the same workspace. 207 208 -lock-timeout=0s Duration to retry a state lock. 209 210 -ignore-remote-version A rare option used for the remote backend only. See 211 the remote backend documentation for more information. 212 213 -state, state-out, and -backup are legacy options supported for the local 214 backend only. For more information, see the local backend's documentation. 215 216 ` 217 return strings.TrimSpace(helpText) 218 } 219 220 func (c *StateReplaceProviderCommand) Synopsis() string { 221 return "Replace provider in the state" 222 }