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  }