github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/command/refresh.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/eliastor/durgaform/internal/backend"
     8  	"github.com/eliastor/durgaform/internal/command/arguments"
     9  	"github.com/eliastor/durgaform/internal/command/views"
    10  	"github.com/eliastor/durgaform/internal/tfdiags"
    11  )
    12  
    13  // RefreshCommand is a cli.Command implementation that refreshes the state
    14  // file.
    15  type RefreshCommand struct {
    16  	Meta
    17  }
    18  
    19  func (c *RefreshCommand) Run(rawArgs []string) int {
    20  	var diags tfdiags.Diagnostics
    21  
    22  	// Parse and apply global view arguments
    23  	common, rawArgs := arguments.ParseView(rawArgs)
    24  	c.View.Configure(common)
    25  
    26  	// Propagate -no-color for legacy use of Ui.  The remote backend and
    27  	// cloud package use this; it should be removed when/if they are
    28  	// migrated to views.
    29  	c.Meta.color = !common.NoColor
    30  	c.Meta.Color = c.Meta.color
    31  
    32  	// Parse and validate flags
    33  	args, diags := arguments.ParseRefresh(rawArgs)
    34  
    35  	// Instantiate the view, even if there are flag errors, so that we render
    36  	// diagnostics according to the desired view
    37  	view := views.NewRefresh(args.ViewType, c.View)
    38  
    39  	if diags.HasErrors() {
    40  		view.Diagnostics(diags)
    41  		view.HelpPrompt()
    42  		return 1
    43  	}
    44  
    45  	// Check for user-supplied plugin path
    46  	var err error
    47  	if c.pluginPath, err = c.loadPluginPath(); err != nil {
    48  		diags = diags.Append(err)
    49  		view.Diagnostics(diags)
    50  		return 1
    51  	}
    52  
    53  	// FIXME: the -input flag value is needed to initialize the backend and the
    54  	// operation, but there is no clear path to pass this value down, so we
    55  	// continue to mutate the Meta object state for now.
    56  	c.Meta.input = args.InputEnabled
    57  
    58  	// FIXME: the -parallelism flag is used to control the concurrency of
    59  	// Durgaform operations. At the moment, this value is used both to
    60  	// initialize the backend via the ContextOpts field inside CLIOpts, and to
    61  	// set a largely unused field on the Operation request. Again, there is no
    62  	// clear path to pass this value down, so we continue to mutate the Meta
    63  	// object state for now.
    64  	c.Meta.parallelism = args.Operation.Parallelism
    65  
    66  	// Prepare the backend with the backend-specific arguments
    67  	be, beDiags := c.PrepareBackend(args.State)
    68  	diags = diags.Append(beDiags)
    69  	if diags.HasErrors() {
    70  		view.Diagnostics(diags)
    71  		return 1
    72  	}
    73  
    74  	// Build the operation request
    75  	opReq, opDiags := c.OperationRequest(be, view, args.Operation)
    76  	diags = diags.Append(opDiags)
    77  	if diags.HasErrors() {
    78  		view.Diagnostics(diags)
    79  		return 1
    80  	}
    81  
    82  	// Collect variable value and add them to the operation request
    83  	diags = diags.Append(c.GatherVariables(opReq, args.Vars))
    84  	if diags.HasErrors() {
    85  		view.Diagnostics(diags)
    86  		return 1
    87  	}
    88  
    89  	// Before we delegate to the backend, we'll print any warning diagnostics
    90  	// we've accumulated here, since the backend will start fresh with its own
    91  	// diagnostics.
    92  	view.Diagnostics(diags)
    93  	diags = nil
    94  
    95  	// Perform the operation
    96  	op, err := c.RunOperation(be, opReq)
    97  	if err != nil {
    98  		diags = diags.Append(err)
    99  		view.Diagnostics(diags)
   100  		return 1
   101  	}
   102  
   103  	if op.State != nil {
   104  		view.Outputs(op.State.RootModule().OutputValues)
   105  	}
   106  
   107  	return op.Result.ExitStatus()
   108  }
   109  
   110  func (c *RefreshCommand) PrepareBackend(args *arguments.State) (backend.Enhanced, tfdiags.Diagnostics) {
   111  	// FIXME: we need to apply the state arguments to the meta object here
   112  	// because they are later used when initializing the backend. Carving a
   113  	// path to pass these arguments to the functions that need them is
   114  	// difficult but would make their use easier to understand.
   115  	c.Meta.applyStateArguments(args)
   116  
   117  	backendConfig, diags := c.loadBackendConfig(".")
   118  	if diags.HasErrors() {
   119  		return nil, diags
   120  	}
   121  
   122  	// Load the backend
   123  	be, beDiags := c.Backend(&BackendOpts{
   124  		Config: backendConfig,
   125  	})
   126  	diags = diags.Append(beDiags)
   127  	if beDiags.HasErrors() {
   128  		return nil, diags
   129  	}
   130  
   131  	return be, diags
   132  }
   133  
   134  func (c *RefreshCommand) OperationRequest(be backend.Enhanced, view views.Refresh, args *arguments.Operation,
   135  ) (*backend.Operation, tfdiags.Diagnostics) {
   136  	var diags tfdiags.Diagnostics
   137  
   138  	// Build the operation
   139  	opReq := c.Operation(be)
   140  	opReq.ConfigDir = "."
   141  	opReq.Hooks = view.Hooks()
   142  	opReq.Targets = args.Targets
   143  	opReq.Type = backend.OperationTypeRefresh
   144  	opReq.View = view.Operation()
   145  
   146  	var err error
   147  	opReq.ConfigLoader, err = c.initConfigLoader()
   148  	if err != nil {
   149  		diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err))
   150  		return nil, diags
   151  	}
   152  
   153  	return opReq, diags
   154  }
   155  
   156  func (c *RefreshCommand) GatherVariables(opReq *backend.Operation, args *arguments.Vars) tfdiags.Diagnostics {
   157  	var diags tfdiags.Diagnostics
   158  
   159  	// FIXME the arguments package currently trivially gathers variable related
   160  	// arguments in a heterogenous slice, in order to minimize the number of
   161  	// code paths gathering variables during the transition to this structure.
   162  	// Once all commands that gather variables have been converted to this
   163  	// structure, we could move the variable gathering code to the arguments
   164  	// package directly, removing this shim layer.
   165  
   166  	varArgs := args.All()
   167  	items := make([]rawFlag, len(varArgs))
   168  	for i := range varArgs {
   169  		items[i].Name = varArgs[i].Name
   170  		items[i].Value = varArgs[i].Value
   171  	}
   172  	c.Meta.variableArgs = rawFlags{items: &items}
   173  	opReq.Variables, diags = c.collectVariableValues()
   174  
   175  	return diags
   176  }
   177  
   178  func (c *RefreshCommand) Help() string {
   179  	helpText := `
   180  Usage: durgaform [global options] refresh [options]
   181  
   182    Update the state file of your infrastructure with metadata that matches
   183    the physical resources they are tracking.
   184  
   185    This will not modify your infrastructure, but it can modify your
   186    state file to update metadata. This metadata might cause new changes
   187    to occur when you generate a plan or call apply next.
   188  
   189  Options:
   190  
   191    -compact-warnings   If Durgaform produces any warnings that are not
   192                        accompanied by errors, show them in a more compact form
   193                        that includes only the summary messages.
   194  
   195    -input=true         Ask for input for variables if not directly set.
   196  
   197    -lock=false         Don't hold a state lock during the operation. This is
   198                        dangerous if others might concurrently run commands
   199                        against the same workspace.
   200  
   201    -lock-timeout=0s    Duration to retry a state lock.
   202  
   203    -no-color           If specified, output won't contain any color.
   204  
   205    -parallelism=n      Limit the number of concurrent operations. Defaults to 10.
   206  
   207    -target=resource    Resource to target. Operation will be limited to this
   208                        resource and its dependencies. This flag can be used
   209                        multiple times.
   210  
   211    -var 'foo=bar'      Set a variable in the Durgaform configuration. This
   212                        flag can be set multiple times.
   213  
   214    -var-file=foo       Set variables in the Durgaform configuration from
   215                        a file. If "durgaform.tfvars" or any ".auto.tfvars"
   216                        files are present, they will be automatically loaded.
   217  
   218    -state, state-out, and -backup are legacy options supported for the local
   219    backend only. For more information, see the local backend's documentation.
   220  `
   221  	return strings.TrimSpace(helpText)
   222  }
   223  
   224  func (c *RefreshCommand) Synopsis() string {
   225  	return "Update the state to match remote systems"
   226  }