github.com/ojongerius/terraform@v0.7.1-0.20160811111335-97fcd5f4cc90/command/taint.go (about)

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