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