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