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