github.com/blacked/terraform@v0.6.2-0.20150806163846-669c4ad71586/command/taint.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  )
     8  
     9  // TaintCommand is a cli.Command implementation that manually taints
    10  // a resource, marking it for recreation.
    11  type TaintCommand struct {
    12  	Meta
    13  }
    14  
    15  func (c *TaintCommand) 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("taint")
    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 taint
    32  	args = cmdFlags.Args()
    33  	if len(args) != 1 {
    34  		c.Ui.Error("The taint 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 tainted."))
    65  		return 1
    66  	}
    67  
    68  	// Get the proper module we want to taint
    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 taint.",
    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 taint.",
    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  	// Taint the resource
   109  	rs.Taint()
   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 marked as tainted!",
   119  		name, module))
   120  	return 0
   121  }
   122  
   123  func (c *TaintCommand) Help() string {
   124  	helpText := `
   125  Usage: terraform taint [options] name
   126  
   127    Manually mark a resource as tainted, forcing a destroy and recreate
   128    on the next plan/apply.
   129  
   130    This will not modify your infrastructure. This command changes your
   131    state to mark a resource as tainted so that during the next plan or
   132    apply, that resource will be destroyed and recreated. This command on
   133    its own will not modify infrastructure. This command can be undone by
   134    reverting the state backup file that is created.
   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 *TaintCommand) Synopsis() string {
   162  	return "Manually mark a resource for recreation"
   163  }
   164  
   165  func (c *TaintCommand) 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  }