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.`