github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/command/untaint.go (about)

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