github.com/d-luu/terraform@v0.11.12-beta1/command/untaint.go (about)

     1  package command
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/command/clistate"
    10  )
    11  
    12  // UntaintCommand is a cli.Command implementation that manually untaints
    13  // a resource, marking it as primary and ready for service.
    14  type UntaintCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *UntaintCommand) Run(args []string) int {
    19  	args, err := c.Meta.process(args, false)
    20  	if err != nil {
    21  		return 1
    22  	}
    23  
    24  	var allowMissing bool
    25  	var module string
    26  	cmdFlags := c.Meta.flagSet("untaint")
    27  	cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module")
    28  	cmdFlags.StringVar(&module, "module", "", "module")
    29  	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
    30  	cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
    31  	cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
    32  	cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
    33  	cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
    34  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    35  	if err := cmdFlags.Parse(args); err != nil {
    36  		return 1
    37  	}
    38  
    39  	// Require the one argument for the resource to untaint
    40  	args = cmdFlags.Args()
    41  	if len(args) != 1 {
    42  		c.Ui.Error("The untaint command expects exactly one argument.")
    43  		cmdFlags.Usage()
    44  		return 1
    45  	}
    46  
    47  	name := args[0]
    48  	if module == "" {
    49  		module = "root"
    50  	} else {
    51  		module = "root." + module
    52  	}
    53  
    54  	// Load the backend
    55  	b, err := c.Backend(nil)
    56  	if err != nil {
    57  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    58  		return 1
    59  	}
    60  
    61  	// Get the state
    62  	env := c.Workspace()
    63  	st, err := b.State(env)
    64  	if err != nil {
    65  		c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
    66  		return 1
    67  	}
    68  
    69  	if c.stateLock {
    70  		stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize())
    71  		if err := stateLocker.Lock(st, "untaint"); err != nil {
    72  			c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
    73  			return 1
    74  		}
    75  		defer stateLocker.Unlock(nil)
    76  	}
    77  
    78  	if err := st.RefreshState(); err != nil {
    79  		c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
    80  		return 1
    81  	}
    82  
    83  	// Get the actual state structure
    84  	s := st.State()
    85  	if s.Empty() {
    86  		if allowMissing {
    87  			return c.allowMissingExit(name, module)
    88  		}
    89  
    90  		c.Ui.Error(fmt.Sprintf(
    91  			"The state is empty. The most common reason for this is that\n" +
    92  				"an invalid state file path was given or Terraform has never\n " +
    93  				"been run for this infrastructure. Infrastructure must exist\n" +
    94  				"for it to be untainted."))
    95  		return 1
    96  	}
    97  
    98  	// Get the proper module holding the resource we want to untaint
    99  	modPath := strings.Split(module, ".")
   100  	mod := s.ModuleByPath(modPath)
   101  	if mod == nil {
   102  		if allowMissing {
   103  			return c.allowMissingExit(name, module)
   104  		}
   105  
   106  		c.Ui.Error(fmt.Sprintf(
   107  			"The module %s could not be found. There is nothing to untaint.",
   108  			module))
   109  		return 1
   110  	}
   111  
   112  	// If there are no resources in this module, it is an error
   113  	if len(mod.Resources) == 0 {
   114  		if allowMissing {
   115  			return c.allowMissingExit(name, module)
   116  		}
   117  
   118  		c.Ui.Error(fmt.Sprintf(
   119  			"The module %s has no resources. There is nothing to untaint.",
   120  			module))
   121  		return 1
   122  	}
   123  
   124  	// Get the resource we're looking for
   125  	rs, ok := mod.Resources[name]
   126  	if !ok {
   127  		if allowMissing {
   128  			return c.allowMissingExit(name, module)
   129  		}
   130  
   131  		c.Ui.Error(fmt.Sprintf(
   132  			"The resource %s couldn't be found in the module %s.",
   133  			name,
   134  			module))
   135  		return 1
   136  	}
   137  
   138  	// Untaint the resource
   139  	rs.Untaint()
   140  
   141  	log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath())
   142  	if err := st.WriteState(s); err != nil {
   143  		c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
   144  		return 1
   145  	}
   146  	if err := st.PersistState(); err != nil {
   147  		c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
   148  		return 1
   149  	}
   150  
   151  	c.Ui.Output(fmt.Sprintf(
   152  		"The resource %s in the module %s has been successfully untainted!",
   153  		name, module))
   154  	return 0
   155  }
   156  
   157  func (c *UntaintCommand) Help() string {
   158  	helpText := `
   159  Usage: terraform untaint [options] name
   160  
   161    Manually unmark a resource as tainted, restoring it as the primary
   162    instance in the state.  This reverses either a manual 'terraform taint'
   163    or the result of provisioners failing on a resource.
   164  
   165    This will not modify your infrastructure. This command changes your
   166    state to unmark a resource as tainted.  This command can be undone by
   167    reverting the state backup file that is created, or by running
   168    'terraform taint' on the resource.
   169  
   170  Options:
   171  
   172    -allow-missing      If specified, the command will succeed (exit code 0)
   173                        even if the resource is missing.
   174  
   175    -backup=path        Path to backup the existing state file before
   176                        modifying. Defaults to the "-state-out" path with
   177                        ".backup" extension. Set to "-" to disable backup.
   178  
   179    -lock=true          Lock the state file when locking is supported.
   180  
   181    -lock-timeout=0s    Duration to retry a state lock.
   182  
   183    -module=path        The module path where the resource lives. By
   184                        default this will be root. Child modules can be specified
   185                        by names. Ex. "consul" or "consul.vpc" (nested modules).
   186  
   187    -no-color           If specified, output won't contain any color.
   188  
   189    -state=path         Path to read and save state (unless state-out
   190                        is specified). Defaults to "terraform.tfstate".
   191  
   192    -state-out=path     Path to write updated state file. By default, the
   193                        "-state" path will be used.
   194  
   195  `
   196  	return strings.TrimSpace(helpText)
   197  }
   198  
   199  func (c *UntaintCommand) Synopsis() string {
   200  	return "Manually unmark a resource as tainted"
   201  }
   202  
   203  func (c *UntaintCommand) allowMissingExit(name, module string) int {
   204  	c.Ui.Output(fmt.Sprintf(
   205  		"The resource %s in the module %s was not found, but\n"+
   206  			"-allow-missing is set, so we're exiting successfully.",
   207  		name, module))
   208  	return 0
   209  }