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