github.com/memsql/terraform@v0.7.0-rc2.0.20160706152241-21e2173e0a32/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 }