github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/arguments/extended.go (about) 1 package arguments 2 3 import ( 4 "flag" 5 "fmt" 6 "time" 7 8 "github.com/hashicorp/hcl/v2" 9 "github.com/hashicorp/hcl/v2/hclsyntax" 10 "github.com/hashicorp/terraform/internal/addrs" 11 "github.com/hashicorp/terraform/internal/plans" 12 "github.com/hashicorp/terraform/internal/tfdiags" 13 ) 14 15 // DefaultParallelism is the limit Terraform places on total parallel 16 // operations as it walks the dependency graph. 17 const DefaultParallelism = 10 18 19 // State describes arguments which are used to define how Terraform interacts 20 // with state. 21 type State struct { 22 // Lock controls whether or not the state manager is used to lock state 23 // during operations. 24 Lock bool 25 26 // LockTimeout allows setting a time limit on acquiring the state lock. 27 // The default is 0, meaning no limit. 28 LockTimeout time.Duration 29 30 // StatePath specifies a non-default location for the state file. The 31 // default value is blank, which is interpeted as "terraform.tfstate". 32 StatePath string 33 34 // StateOutPath specifies a different path to write the final state file. 35 // The default value is blank, which results in state being written back to 36 // StatePath. 37 StateOutPath string 38 39 // BackupPath specifies the path where a backup copy of the state file will 40 // be stored before the new state is written. The default value is blank, 41 // which is interpreted as StateOutPath + 42 // ".backup". 43 BackupPath string 44 } 45 46 // Operation describes arguments which are used to configure how a Terraform 47 // operation such as a plan or apply executes. 48 type Operation struct { 49 // PlanMode selects one of the mutually-exclusive planning modes that 50 // decides the overall goal of a plan operation. This field is relevant 51 // only for an operation that produces a plan. 52 PlanMode plans.Mode 53 54 // Parallelism is the limit Terraform places on total parallel operations 55 // as it walks the dependency graph. 56 Parallelism int 57 58 // Refresh controls whether or not the operation should refresh existing 59 // state before proceeding. Default is true. 60 Refresh bool 61 62 // Targets allow limiting an operation to a set of resource addresses and 63 // their dependencies. 64 Targets []addrs.Targetable 65 66 // ForceReplace addresses cause Terraform to force a particular set of 67 // resource instances to generate "replace" actions in any plan where they 68 // would normally have generated "no-op" or "update" actions. 69 // 70 // This is currently limited to specific instances because typical uses 71 // of replace are associated with only specific remote objects that the 72 // user has somehow learned to be malfunctioning, in which case it 73 // would be unusual and potentially dangerous to replace everything under 74 // a module all at once. We could potentially loosen this later if we 75 // learn a use-case for broader matching. 76 ForceReplace []addrs.AbsResourceInstance 77 78 // These private fields are used only temporarily during decoding. Use 79 // method Parse to populate the exported fields from these, validating 80 // the raw values in the process. 81 targetsRaw []string 82 forceReplaceRaw []string 83 destroyRaw bool 84 refreshOnlyRaw bool 85 } 86 87 // Parse must be called on Operation after initial flag parse. This processes 88 // the raw target flags into addrs.Targetable values, returning diagnostics if 89 // invalid. 90 func (o *Operation) Parse() tfdiags.Diagnostics { 91 var diags tfdiags.Diagnostics 92 93 o.Targets = nil 94 95 for _, tr := range o.targetsRaw { 96 traversal, syntaxDiags := hclsyntax.ParseTraversalAbs([]byte(tr), "", hcl.Pos{Line: 1, Column: 1}) 97 if syntaxDiags.HasErrors() { 98 diags = diags.Append(tfdiags.Sourceless( 99 tfdiags.Error, 100 fmt.Sprintf("Invalid target %q", tr), 101 syntaxDiags[0].Detail, 102 )) 103 continue 104 } 105 106 target, targetDiags := addrs.ParseTarget(traversal) 107 if targetDiags.HasErrors() { 108 diags = diags.Append(tfdiags.Sourceless( 109 tfdiags.Error, 110 fmt.Sprintf("Invalid target %q", tr), 111 targetDiags[0].Description().Detail, 112 )) 113 continue 114 } 115 116 o.Targets = append(o.Targets, target.Subject) 117 } 118 119 for _, raw := range o.forceReplaceRaw { 120 traversal, syntaxDiags := hclsyntax.ParseTraversalAbs([]byte(raw), "", hcl.Pos{Line: 1, Column: 1}) 121 if syntaxDiags.HasErrors() { 122 diags = diags.Append(tfdiags.Sourceless( 123 tfdiags.Error, 124 fmt.Sprintf("Invalid force-replace address %q", raw), 125 syntaxDiags[0].Detail, 126 )) 127 continue 128 } 129 130 addr, addrDiags := addrs.ParseAbsResourceInstance(traversal) 131 if addrDiags.HasErrors() { 132 diags = diags.Append(tfdiags.Sourceless( 133 tfdiags.Error, 134 fmt.Sprintf("Invalid force-replace address %q", raw), 135 addrDiags[0].Description().Detail, 136 )) 137 continue 138 } 139 140 if addr.Resource.Resource.Mode != addrs.ManagedResourceMode { 141 diags = diags.Append(tfdiags.Sourceless( 142 tfdiags.Error, 143 fmt.Sprintf("Invalid force-replace address %q", raw), 144 "Only managed resources can be used with the -replace=... option.", 145 )) 146 continue 147 } 148 149 o.ForceReplace = append(o.ForceReplace, addr) 150 } 151 152 // If you add a new possible value for o.PlanMode here, consider also 153 // adding a specialized error message for it in ParseApplyDestroy. 154 switch { 155 case o.destroyRaw && o.refreshOnlyRaw: 156 diags = diags.Append(tfdiags.Sourceless( 157 tfdiags.Error, 158 "Incompatible plan mode options", 159 "The -destroy and -refresh-only options are mutually-exclusive.", 160 )) 161 case o.destroyRaw: 162 o.PlanMode = plans.DestroyMode 163 case o.refreshOnlyRaw: 164 o.PlanMode = plans.RefreshOnlyMode 165 if !o.Refresh { 166 diags = diags.Append(tfdiags.Sourceless( 167 tfdiags.Error, 168 "Incompatible refresh options", 169 "It doesn't make sense to use -refresh-only at the same time as -refresh=false, because Terraform would have nothing to do.", 170 )) 171 } 172 default: 173 o.PlanMode = plans.NormalMode 174 } 175 176 return diags 177 } 178 179 // Vars describes arguments which specify non-default variable values. This 180 // interfce is unfortunately obscure, because the order of the CLI arguments 181 // determines the final value of the gathered variables. In future it might be 182 // desirable for the arguments package to handle the gathering of variables 183 // directly, returning a map of variable values. 184 type Vars struct { 185 vars *flagNameValueSlice 186 varFiles *flagNameValueSlice 187 } 188 189 func (v *Vars) All() []FlagNameValue { 190 if v.vars == nil { 191 return nil 192 } 193 return v.vars.AllItems() 194 } 195 196 func (v *Vars) Empty() bool { 197 if v.vars == nil { 198 return true 199 } 200 return v.vars.Empty() 201 } 202 203 // extendedFlagSet creates a FlagSet with common backend, operation, and vars 204 // flags used in many commands. Target structs for each subset of flags must be 205 // provided in order to support those flags. 206 func extendedFlagSet(name string, state *State, operation *Operation, vars *Vars) *flag.FlagSet { 207 f := defaultFlagSet(name) 208 209 if state == nil && operation == nil && vars == nil { 210 panic("use defaultFlagSet") 211 } 212 213 if state != nil { 214 f.BoolVar(&state.Lock, "lock", true, "lock") 215 f.DurationVar(&state.LockTimeout, "lock-timeout", 0, "lock-timeout") 216 f.StringVar(&state.StatePath, "state", "", "state-path") 217 f.StringVar(&state.StateOutPath, "state-out", "", "state-path") 218 f.StringVar(&state.BackupPath, "backup", "", "backup-path") 219 } 220 221 if operation != nil { 222 f.IntVar(&operation.Parallelism, "parallelism", DefaultParallelism, "parallelism") 223 f.BoolVar(&operation.Refresh, "refresh", true, "refresh") 224 f.BoolVar(&operation.destroyRaw, "destroy", false, "destroy") 225 f.BoolVar(&operation.refreshOnlyRaw, "refresh-only", false, "refresh-only") 226 f.Var((*flagStringSlice)(&operation.targetsRaw), "target", "target") 227 f.Var((*flagStringSlice)(&operation.forceReplaceRaw), "replace", "replace") 228 } 229 230 // Gather all -var and -var-file arguments into one heterogenous structure 231 // to preserve the overall order. 232 if vars != nil { 233 varsFlags := newFlagNameValueSlice("-var") 234 varFilesFlags := varsFlags.Alias("-var-file") 235 vars.vars = &varsFlags 236 vars.varFiles = &varFilesFlags 237 f.Var(vars.vars, "var", "var") 238 f.Var(vars.varFiles, "var-file", "var-file") 239 } 240 241 return f 242 }