From e043d35b60605b045db3335089d96e7171f8b1d1 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 18 Sep 2023 14:38:05 +0100 Subject: [PATCH] Introduce validation of tfvars files (WIP) --- internal/indexer/document_change.go | 20 ++++++ internal/indexer/document_open.go | 21 ++++++ internal/state/module.go | 2 +- internal/terraform/module/module_ops.go | 70 ++++++++++++++++++- .../module/operation/op_type_string.go | 9 +-- .../terraform/module/operation/operation.go | 1 + 6 files changed, 116 insertions(+), 7 deletions(-) diff --git a/internal/indexer/document_change.go b/internal/indexer/document_change.go index 2dc243fff..19b1cceb1 100644 --- a/internal/indexer/document_change.go +++ b/internal/indexer/document_change.go @@ -49,6 +49,26 @@ func (idx *Indexer) DocumentChanged(ctx context.Context, modHandle document.DirH } ids = append(ids, parseVarsId) + validationOptions, err := lsctx.ValidationOptions(ctx) + if err != nil { + return ids, err + } + + if validationOptions.EarlyValidation { + _, err = idx.jobStore.EnqueueJob(ctx, job.Job{ + Dir: modHandle, + Func: func(ctx context.Context) error { + return module.SchemaVariablesValidation(ctx, idx.modStore, idx.schemaStore, modHandle.Path()) + }, + Type: op.OpTypeSchemaVarsValidation.String(), + DependsOn: append(modIds, parseVarsId), + IgnoreState: true, + }) + if err != nil { + return ids, err + } + } + varsRefsId, err := idx.jobStore.EnqueueJob(ctx, job.Job{ Dir: modHandle, Func: func(ctx context.Context) error { diff --git a/internal/indexer/document_open.go b/internal/indexer/document_open.go index 159c2ddec..043840d05 100644 --- a/internal/indexer/document_open.go +++ b/internal/indexer/document_open.go @@ -7,6 +7,7 @@ import ( "context" "github.com/hashicorp/go-multierror" + lsctx "github.com/hashicorp/terraform-ls/internal/context" "github.com/hashicorp/terraform-ls/internal/document" "github.com/hashicorp/terraform-ls/internal/job" "github.com/hashicorp/terraform-ls/internal/terraform/exec" @@ -72,6 +73,26 @@ func (idx *Indexer) DocumentOpened(ctx context.Context, modHandle document.DirHa } ids = append(ids, parseVarsId) + validationOptions, err := lsctx.ValidationOptions(ctx) + if err != nil { + return ids, err + } + + if validationOptions.EarlyValidation { + _, err = idx.jobStore.EnqueueJob(ctx, job.Job{ + Dir: modHandle, + Func: func(ctx context.Context) error { + return module.SchemaVariablesValidation(ctx, idx.modStore, idx.schemaStore, modHandle.Path()) + }, + Type: op.OpTypeSchemaVarsValidation.String(), + DependsOn: append(modIds, parseVarsId), + IgnoreState: true, + }) + if err != nil { + return ids, err + } + } + varsRefsId, err := idx.jobStore.EnqueueJob(ctx, job.Job{ Dir: modHandle, Func: func(ctx context.Context) error { diff --git a/internal/state/module.go b/internal/state/module.go index b06765b36..10838bc06 100644 --- a/internal/state/module.go +++ b/internal/state/module.go @@ -988,7 +988,7 @@ func (s *ModuleStore) SetModuleDiagnosticsState(path string, source ast.Diagnost func (s *ModuleStore) UpdateVarsDiagnostics(path string, source ast.DiagnosticSource, diags ast.VarsDiags) error { txn := s.db.Txn(true) txn.Defer(func() { - s.SetVarsDiagnosticsState(path, ast.HCLParsingSource, op.OpStateLoaded) + s.SetVarsDiagnosticsState(path, source, op.OpStateLoaded) }) defer txn.Abort() diff --git a/internal/terraform/module/module_ops.go b/internal/terraform/module/module_ops.go index 866e1263d..27e1fcb96 100644 --- a/internal/terraform/module/module_ops.go +++ b/internal/terraform/module/module_ops.go @@ -688,6 +688,8 @@ func SchemaValidation(ctx context.Context, modStore *state.ModuleStore, schemaRe ModuleReader: modStore, SchemaReader: schemaReader, }) + + // TODO: specify validators d.SetContext(idecoder.DecoderContext(ctx)) moduleDecoder, err := d.Path(lang.Path{ @@ -700,8 +702,72 @@ func SchemaValidation(ctx context.Context, modStore *state.ModuleStore, schemaRe var rErr error rpcContext := lsctx.RPCContext(ctx) - isSingleFileChange := rpcContext.Method == "textDocument/didChange" - if isSingleFileChange { + if rpcContext.Method == "textDocument/didChange" && lsctx.IsLanguageId(ctx, ilsp.Terraform.String()) { + filename := path.Base(rpcContext.URI) + // We only revalidate a single file that changed + var fileDiags hcl.Diagnostics + fileDiags, rErr = moduleDecoder.ValidateFile(ctx, filename) + + modDiags, ok := mod.ModuleDiagnostics[ast.SchemaValidationSource] + if !ok { + modDiags = make(ast.ModDiags) + } + modDiags[ast.ModFilename(filename)] = fileDiags + + sErr := modStore.UpdateModuleDiagnostics(modPath, ast.SchemaValidationSource, modDiags) + if sErr != nil { + return sErr + } + } else { + // We validate the whole module, e.g. on open + var diags lang.DiagnosticsMap + diags, rErr = moduleDecoder.Validate(ctx) + + sErr := modStore.UpdateModuleDiagnostics(modPath, ast.SchemaValidationSource, ast.ModDiagsFromMap(diags)) + if sErr != nil { + return sErr + } + } + + return rErr +} + +func SchemaVariablesValidation(ctx context.Context, modStore *state.ModuleStore, schemaReader state.SchemaReader, modPath string) error { + // TODO + mod, err := modStore.ModuleByPath(modPath) + if err != nil { + return err + } + + // Avoid validation if it is already in progress or already finished + if mod.ModuleDiagnosticsState[ast.SchemaValidationSource] != op.OpStateUnknown && !job.IgnoreState(ctx) { + return job.StateNotChangedErr{Dir: document.DirHandleFromPath(modPath)} + } + + err = modStore.SetModuleDiagnosticsState(modPath, ast.SchemaValidationSource, op.OpStateLoading) + if err != nil { + return err + } + + d := decoder.NewDecoder(&idecoder.PathReader{ + ModuleReader: modStore, + SchemaReader: schemaReader, + }) + + // TODO: specify validators + d.SetContext(idecoder.DecoderContext(ctx)) + + moduleDecoder, err := d.Path(lang.Path{ + Path: modPath, + LanguageID: ilsp.Tfvars.String(), + }) + if err != nil { + return err + } + + var rErr error + rpcContext := lsctx.RPCContext(ctx) + if rpcContext.Method == "textDocument/didChange" && lsctx.IsLanguageId(ctx, ilsp.Tfvars.String()) { filename := path.Base(rpcContext.URI) // We only revalidate a single file that changed var fileDiags hcl.Diagnostics diff --git a/internal/terraform/module/operation/op_type_string.go b/internal/terraform/module/operation/op_type_string.go index 5b4ac8744..77e71b833 100644 --- a/internal/terraform/module/operation/op_type_string.go +++ b/internal/terraform/module/operation/op_type_string.go @@ -22,13 +22,14 @@ func _() { _ = x[OpTypeParseProviderVersions-11] _ = x[OpTypePreloadEmbeddedSchema-12] _ = x[OpTypeSchemaValidation-13] - _ = x[OpTypeReferenceValidation-14] - _ = x[OpTypeTerraformValidate-15] + _ = x[OpTypeSchemaVarsValidation-14] + _ = x[OpTypeReferenceValidation-15] + _ = x[OpTypeTerraformValidate-16] } -const _OpType_name = "OpTypeUnknownOpTypeGetTerraformVersionOpTypeObtainSchemaOpTypeParseModuleConfigurationOpTypeParseVariablesOpTypeParseModuleManifestOpTypeLoadModuleMetadataOpTypeDecodeReferenceTargetsOpTypeDecodeReferenceOriginsOpTypeDecodeVarsReferencesOpTypeGetModuleDataFromRegistryOpTypeParseProviderVersionsOpTypePreloadEmbeddedSchemaOpTypeSchemaValidationOpTypeReferenceValidationOpTypeTerraformValidate" +const _OpType_name = "OpTypeUnknownOpTypeGetTerraformVersionOpTypeObtainSchemaOpTypeParseModuleConfigurationOpTypeParseVariablesOpTypeParseModuleManifestOpTypeLoadModuleMetadataOpTypeDecodeReferenceTargetsOpTypeDecodeReferenceOriginsOpTypeDecodeVarsReferencesOpTypeGetModuleDataFromRegistryOpTypeParseProviderVersionsOpTypePreloadEmbeddedSchemaOpTypeSchemaValidationOpTypeSchemaVarsValidationOpTypeReferenceValidationOpTypeTerraformValidate" -var _OpType_index = [...]uint16{0, 13, 38, 56, 86, 106, 131, 155, 183, 211, 237, 268, 295, 322, 344, 369, 392} +var _OpType_index = [...]uint16{0, 13, 38, 56, 86, 106, 131, 155, 183, 211, 237, 268, 295, 322, 344, 370, 395, 418} func (i OpType) String() string { if i >= OpType(len(_OpType_index)-1) { diff --git a/internal/terraform/module/operation/operation.go b/internal/terraform/module/operation/operation.go index fe408450d..c33a27d99 100644 --- a/internal/terraform/module/operation/operation.go +++ b/internal/terraform/module/operation/operation.go @@ -31,6 +31,7 @@ const ( OpTypeParseProviderVersions OpTypePreloadEmbeddedSchema OpTypeSchemaValidation + OpTypeSchemaVarsValidation OpTypeReferenceValidation OpTypeTerraformValidate )