github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/command/untaint.go (about)

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