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