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 }