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