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