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