Skip to content

Commit

Permalink
Introduce validation of tfvars files (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Sep 19, 2023
1 parent 09033c8 commit 439f17a
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 7 deletions.
2 changes: 2 additions & 0 deletions internal/decoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func modulePathContext(mod *state.Module, schemaReader state.SchemaReader, modRe
ReferenceTargets: make(reference.Targets, 0),
Files: make(map[string]*hcl.File, 0),
Functions: coreFunctions(mod),
Validators: moduleValidators,
}

for _, origin := range mod.RefOrigins {
Expand Down Expand Up @@ -63,6 +64,7 @@ func varsPathContext(mod *state.Module) (*decoder.PathContext, error) {
ReferenceOrigins: make(reference.Origins, 0),
ReferenceTargets: make(reference.Targets, 0),
Files: make(map[string]*hcl.File),
// TODO: Validators: varsValidators
}

for _, origin := range mod.VarsRefOrigins {
Expand Down
16 changes: 16 additions & 0 deletions internal/decoder/validators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package decoder

import (
"github.com/hashicorp/hcl-lang/validator"
)

var moduleValidators = []validator.Validator{
validator.BlockLabelsLength{},
validator.DeprecatedAttribute{},
validator.DeprecatedBlock{},
validator.MaxBlocks{},
validator.MinBlocks{},
validator.MissingRequiredAttribute{},
validator.UnexpectedAttribute{},
validator.UnexpectedBlock{},
}
20 changes: 20 additions & 0 deletions internal/indexer/document_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 21 additions & 0 deletions internal/indexer/document_open.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion internal/state/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
67 changes: 65 additions & 2 deletions internal/terraform/module/module_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ func SchemaValidation(ctx context.Context, modStore *state.ModuleStore, schemaRe
ModuleReader: modStore,
SchemaReader: schemaReader,
})

d.SetContext(idecoder.DecoderContext(ctx))

moduleDecoder, err := d.Path(lang.Path{
Expand All @@ -700,8 +701,7 @@ 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
Expand Down Expand Up @@ -731,6 +731,69 @@ func SchemaValidation(ctx context.Context, modStore *state.ModuleStore, schemaRe
return rErr
}

func SchemaVariablesValidation(ctx context.Context, modStore *state.ModuleStore, schemaReader state.SchemaReader, modPath string) error {
mod, err := modStore.ModuleByPath(modPath)
if err != nil {
return err
}

// Avoid validation if it is already in progress or already finished
if mod.VarsDiagnosticsState[ast.SchemaValidationSource] != op.OpStateUnknown && !job.IgnoreState(ctx) {
return job.StateNotChangedErr{Dir: document.DirHandleFromPath(modPath)}
}

err = modStore.SetVarsDiagnosticsState(modPath, ast.SchemaValidationSource, op.OpStateLoading)
if err != nil {
return err
}

d := decoder.NewDecoder(&idecoder.PathReader{
ModuleReader: modStore,
SchemaReader: schemaReader,
})

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
fileDiags, rErr = moduleDecoder.ValidateFile(ctx, filename)

varsDiags, ok := mod.VarsDiagnostics[ast.SchemaValidationSource]
if !ok {
varsDiags = make(ast.VarsDiags)
}
varsDiags[ast.VarsFilename(filename)] = fileDiags

sErr := modStore.UpdateVarsDiagnostics(modPath, ast.SchemaValidationSource, varsDiags)
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.UpdateVarsDiagnostics(modPath, ast.SchemaValidationSource, ast.VarsDiagsFromMap(diags))
if sErr != nil {
return sErr
}
}

return rErr
}

func ReferenceValidation(ctx context.Context, modStore *state.ModuleStore, schemaReader state.SchemaReader, modPath string) error {
mod, err := modStore.ModuleByPath(modPath)
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions internal/terraform/module/operation/op_type_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/terraform/module/operation/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
OpTypeParseProviderVersions
OpTypePreloadEmbeddedSchema
OpTypeSchemaValidation
OpTypeSchemaVarsValidation
OpTypeReferenceValidation
OpTypeTerraformValidate
)

0 comments on commit 439f17a

Please sign in to comment.