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