github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/command/validate.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/arguments"
    11  	"github.com/iaas-resource-provision/iaas-rpc/internal/command/views"
    12  	"github.com/iaas-resource-provision/iaas-rpc/internal/terraform"
    13  	"github.com/iaas-resource-provision/iaas-rpc/internal/tfdiags"
    14  )
    15  
    16  // ValidateCommand is a Command implementation that validates the terraform files
    17  type ValidateCommand struct {
    18  	Meta
    19  }
    20  
    21  func (c *ValidateCommand) Run(rawArgs []string) int {
    22  	// Parse and apply global view arguments
    23  	common, rawArgs := arguments.ParseView(rawArgs)
    24  	c.View.Configure(common)
    25  
    26  	// Parse and validate flags
    27  	args, diags := arguments.ParseValidate(rawArgs)
    28  	if diags.HasErrors() {
    29  		c.View.Diagnostics(diags)
    30  		c.View.HelpPrompt("validate")
    31  		return 1
    32  	}
    33  
    34  	view := views.NewValidate(args.ViewType, c.View)
    35  
    36  	// After this point, we must only produce JSON output if JSON mode is
    37  	// enabled, so all errors should be accumulated into diags and we'll
    38  	// print out a suitable result at the end, depending on the format
    39  	// selection. All returns from this point on must be tail-calls into
    40  	// view.Results in order to produce the expected output.
    41  
    42  	dir, err := filepath.Abs(args.Path)
    43  	if err != nil {
    44  		diags = diags.Append(fmt.Errorf("unable to locate module: %s", err))
    45  		return view.Results(diags)
    46  	}
    47  
    48  	// Check for user-supplied plugin path
    49  	if c.pluginPath, err = c.loadPluginPath(); err != nil {
    50  		diags = diags.Append(fmt.Errorf("error loading plugin path: %s", err))
    51  		return view.Results(diags)
    52  	}
    53  
    54  	validateDiags := c.validate(dir)
    55  	diags = diags.Append(validateDiags)
    56  
    57  	// Validating with dev overrides in effect means that the result might
    58  	// not be valid for a stable release, so we'll warn about that in case
    59  	// the user is trying to use "terraform validate" as a sort of pre-flight
    60  	// check before submitting a change.
    61  	diags = diags.Append(c.providerDevOverrideRuntimeWarnings())
    62  
    63  	return view.Results(diags)
    64  }
    65  
    66  func (c *ValidateCommand) validate(dir string) tfdiags.Diagnostics {
    67  	var diags tfdiags.Diagnostics
    68  
    69  	cfg, cfgDiags := c.loadConfig(dir)
    70  	diags = diags.Append(cfgDiags)
    71  
    72  	if diags.HasErrors() {
    73  		return diags
    74  	}
    75  
    76  	// "validate" is to check if the given module is valid regardless of
    77  	// input values, current state, etc. Therefore we populate all of the
    78  	// input values with unknown values of the expected type, allowing us
    79  	// to perform a type check without assuming any particular values.
    80  	varValues := make(terraform.InputValues)
    81  	for name, variable := range cfg.Module.Variables {
    82  		ty := variable.Type
    83  		if ty == cty.NilType {
    84  			// Can't predict the type at all, so we'll just mark it as
    85  			// cty.DynamicVal (unknown value of cty.DynamicPseudoType).
    86  			ty = cty.DynamicPseudoType
    87  		}
    88  		varValues[name] = &terraform.InputValue{
    89  			Value:      cty.UnknownVal(ty),
    90  			SourceType: terraform.ValueFromCLIArg,
    91  		}
    92  	}
    93  
    94  	opts, err := c.contextOpts()
    95  	if err != nil {
    96  		diags = diags.Append(err)
    97  		return diags
    98  	}
    99  	opts.Config = cfg
   100  	opts.Variables = varValues
   101  
   102  	tfCtx, ctxDiags := terraform.NewContext(opts)
   103  	diags = diags.Append(ctxDiags)
   104  	if ctxDiags.HasErrors() {
   105  		return diags
   106  	}
   107  
   108  	validateDiags := tfCtx.Validate()
   109  	diags = diags.Append(validateDiags)
   110  	return diags
   111  }
   112  
   113  func (c *ValidateCommand) Synopsis() string {
   114  	return "Check whether the configuration is valid"
   115  }
   116  
   117  func (c *ValidateCommand) Help() string {
   118  	helpText := `
   119  Usage: terraform [global options] validate [options]
   120  
   121    Validate the configuration files in a directory, referring only to the
   122    configuration and not accessing any remote services such as remote state,
   123    provider APIs, etc.
   124  
   125    Validate runs checks that verify whether a configuration is syntactically
   126    valid and internally consistent, regardless of any provided variables or
   127    existing state. It is thus primarily useful for general verification of
   128    reusable modules, including correctness of attribute names and value types.
   129  
   130    It is safe to run this command automatically, for example as a post-save
   131    check in a text editor or as a test step for a re-usable module in a CI
   132    system.
   133  
   134    Validation requires an initialized working directory with any referenced
   135    plugins and modules installed. To initialize a working directory for
   136    validation without accessing any configured remote backend, use:
   137        terraform init -backend=false
   138  
   139    To verify configuration in the context of a particular run (a particular
   140    target workspace, input variable values, etc), use the 'terraform plan'
   141    command instead, which includes an implied validation check.
   142  
   143  Options:
   144  
   145    -json        Produce output in a machine-readable JSON format, suitable for
   146                 use in text editor integrations and other automated systems.
   147                 Always disables color.
   148  
   149    -no-color    If specified, output won't contain any color.
   150  `
   151  	return strings.TrimSpace(helpText)
   152  }