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