github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/command/state_rm.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/iaas-resource-provision/iaas-rpc/internal/addrs" 8 "github.com/iaas-resource-provision/iaas-rpc/internal/command/arguments" 9 "github.com/iaas-resource-provision/iaas-rpc/internal/command/clistate" 10 "github.com/iaas-resource-provision/iaas-rpc/internal/command/views" 11 "github.com/iaas-resource-provision/iaas-rpc/internal/tfdiags" 12 "github.com/mitchellh/cli" 13 ) 14 15 // StateRmCommand is a Command implementation that shows a single resource. 16 type StateRmCommand struct { 17 StateMeta 18 } 19 20 func (c *StateRmCommand) Run(args []string) int { 21 args = c.Meta.process(args) 22 var dryRun bool 23 cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state rm") 24 cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run") 25 cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup") 26 cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") 27 cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") 28 cmdFlags.StringVar(&c.statePath, "state", "", "path") 29 if err := cmdFlags.Parse(args); err != nil { 30 c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) 31 return 1 32 } 33 34 args = cmdFlags.Args() 35 if len(args) < 1 { 36 c.Ui.Error("At least one address is required.\n") 37 return cli.RunResultHelp 38 } 39 40 // Get the state 41 stateMgr, err := c.State() 42 if err != nil { 43 c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) 44 return 1 45 } 46 47 if c.stateLock { 48 stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) 49 if diags := stateLocker.Lock(stateMgr, "state-rm"); diags.HasErrors() { 50 c.showDiagnostics(diags) 51 return 1 52 } 53 defer func() { 54 if diags := stateLocker.Unlock(); diags.HasErrors() { 55 c.showDiagnostics(diags) 56 } 57 }() 58 } 59 60 if err := stateMgr.RefreshState(); err != nil { 61 c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err)) 62 return 1 63 } 64 65 state := stateMgr.State() 66 if state == nil { 67 c.Ui.Error(errStateNotFound) 68 return 1 69 } 70 71 // This command primarily works with resource instances, though it will 72 // also clean up any modules and resources left empty by actions it takes. 73 var addrs []addrs.AbsResourceInstance 74 var diags tfdiags.Diagnostics 75 for _, addrStr := range args { 76 moreAddrs, moreDiags := c.lookupResourceInstanceAddr(state, true, addrStr) 77 addrs = append(addrs, moreAddrs...) 78 diags = diags.Append(moreDiags) 79 } 80 if diags.HasErrors() { 81 c.showDiagnostics(diags) 82 return 1 83 } 84 85 prefix := "Removed " 86 if dryRun { 87 prefix = "Would remove " 88 } 89 90 var isCount int 91 ss := state.SyncWrapper() 92 for _, addr := range addrs { 93 isCount++ 94 c.Ui.Output(prefix + addr.String()) 95 if !dryRun { 96 ss.ForgetResourceInstanceAll(addr) 97 ss.RemoveResourceIfEmpty(addr.ContainingResource()) 98 } 99 } 100 101 if dryRun { 102 if isCount == 0 { 103 c.Ui.Output("Would have removed nothing.") 104 } 105 return 0 // This is as far as we go in dry-run mode 106 } 107 108 if err := stateMgr.WriteState(state); err != nil { 109 c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) 110 return 1 111 } 112 if err := stateMgr.PersistState(); err != nil { 113 c.Ui.Error(fmt.Sprintf(errStateRmPersist, err)) 114 return 1 115 } 116 117 if len(diags) > 0 && isCount != 0 { 118 c.showDiagnostics(diags) 119 } 120 121 if isCount == 0 { 122 diags = diags.Append(tfdiags.Sourceless( 123 tfdiags.Error, 124 "Invalid target address", 125 "No matching objects found. To view the available instances, use \"terraform state list\". Please modify the address to reference a specific instance.", 126 )) 127 c.showDiagnostics(diags) 128 return 1 129 } 130 131 c.Ui.Output(fmt.Sprintf("Successfully removed %d resource instance(s).", isCount)) 132 return 0 133 } 134 135 func (c *StateRmCommand) Help() string { 136 helpText := ` 137 Usage: terraform [global options] state rm [options] ADDRESS... 138 139 Remove one or more items from the Terraform state, causing Terraform to 140 "forget" those items without first destroying them in the remote system. 141 142 This command removes one or more resource instances from the Terraform state 143 based on the addresses given. You can view and list the available instances 144 with "terraform state list". 145 146 If you give the address of an entire module then all of the instances in 147 that module and any of its child modules will be removed from the state. 148 149 If you give the address of a resource that has "count" or "for_each" set, 150 all of the instances of that resource will be removed from the state. 151 152 Options: 153 154 -dry-run If set, prints out what would've been removed but 155 doesn't actually remove anything. 156 157 -backup=PATH Path where Terraform should write the backup 158 state. 159 160 -lock=false Don't hold a state lock during the operation. This is 161 dangerous if others might concurrently run commands 162 against the same workspace. 163 164 -lock-timeout=0s Duration to retry a state lock. 165 166 -state=PATH Path to the state file to update. Defaults to the 167 current workspace state. 168 169 -ignore-remote-version Continue even if remote and local Terraform versions 170 are incompatible. This may result in an unusable 171 workspace, and should be used with extreme caution. 172 173 ` 174 return strings.TrimSpace(helpText) 175 } 176 177 func (c *StateRmCommand) Synopsis() string { 178 return "Remove instances from the state" 179 } 180 181 const errStateRmPersist = `Error saving the state: %s 182 183 The state was not saved. No items were removed from the persisted 184 state. No backup was created since no modification occurred. Please 185 resolve the issue above and try again.`