Skip to content

Commit

Permalink
Implement module validation on save
Browse files Browse the repository at this point in the history
  • Loading branch information
appilon committed Dec 6, 2020
1 parent a606157 commit 42af1b9
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 27 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ require (
github.com/creachadair/jrpc2 v0.11.0
github.com/fsnotify/fsnotify v1.4.9
github.com/gammazero/workerpool v1.0.0
github.com/google/go-cmp v0.5.1
github.com/google/go-cmp v0.5.2
github.com/google/uuid v1.1.2
github.com/hashicorp/go-multierror v1.1.0
github.com/hashicorp/go-version v1.2.1
github.com/hashicorp/hcl-lang v0.0.0-20201116081236-948e43712a65
github.com/hashicorp/hcl/v2 v2.6.0
github.com/hashicorp/terraform-exec v0.11.1-0.20201007122305-ea2094d52cb5
github.com/hashicorp/terraform-exec v0.11.1-0.20201201215927-6e0fd8a457b8
github.com/hashicorp/terraform-json v0.6.0
github.com/hashicorp/terraform-schema v0.0.0-20201204171308-0c9744a02c65
github.com/mh-cbon/go-fmt-fail v0.0.0-20160815164508-67765b3fbcb5
Expand All @@ -24,6 +24,6 @@ require (
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
github.com/spf13/afero v1.3.2
github.com/stretchr/testify v1.4.0
github.com/stretchr/testify v1.6.1
github.com/vektra/mockery/v2 v2.3.0
)
12 changes: 10 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
Expand Down Expand Up @@ -196,8 +198,10 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/terraform-config-inspect v0.0.0-20201102131242-0c45ba392e51 h1:SEGO1vz/pFLfKy4QpABIMCe7wffmtsOiWO4yc1E87cU=
github.com/hashicorp/terraform-config-inspect v0.0.0-20201102131242-0c45ba392e51/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs=
github.com/hashicorp/terraform-exec v0.11.1-0.20201007122305-ea2094d52cb5 h1:P+lBGicJEG3ijvOrDdQf/Oo8UrG4QAJbdY3g9OGBnr0=
github.com/hashicorp/terraform-exec v0.11.1-0.20201007122305-ea2094d52cb5/go.mod h1:eQdBvA0Xr/ZJNilY8TzrtePLSqLyexk9PSwVwzzHTjY=
github.com/hashicorp/terraform-exec v0.11.1-0.20201126033416-f9d65bf2bca2 h1:WNNF6oK2BXG0nGalTXDjP2XwdWxARRtGuN2Ih1ECIn8=
github.com/hashicorp/terraform-exec v0.11.1-0.20201126033416-f9d65bf2bca2/go.mod h1:162lr1MQN7XkrqevlStXPonCGMP5yvyG9zWnlmGdQLI=
github.com/hashicorp/terraform-exec v0.11.1-0.20201201215927-6e0fd8a457b8 h1:sJXjDnxrrpmmJcAiPqX1HL7uksBgVXjY+3VX6k09zkA=
github.com/hashicorp/terraform-exec v0.11.1-0.20201201215927-6e0fd8a457b8/go.mod h1:162lr1MQN7XkrqevlStXPonCGMP5yvyG9zWnlmGdQLI=
github.com/hashicorp/terraform-json v0.5.0 h1:7TV3/F3y7QVSuN4r9BEXqnWqrAyeOtON8f0wvREtyzs=
github.com/hashicorp/terraform-json v0.5.0/go.mod h1:eAbqb4w0pSlRmdvl8fOyHAi/+8jnkVYN28gJkSJrLhU=
github.com/hashicorp/terraform-json v0.6.0 h1:nMTj4t9ysC7xJ72rvVsDqhUccvbUINrjhPqafeUeREk=
Expand Down Expand Up @@ -351,6 +355,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
Expand Down Expand Up @@ -551,6 +557,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
55 changes: 40 additions & 15 deletions internal/langserver/diagnostics/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,28 @@ import (
type diagContext struct {
ctx context.Context
uri lsp.DocumentURI
diags hcl.Diagnostics
source string
diags []lsp.Diagnostic
}

// Notifier is a type responsible for queueing hcl diagnostics to be converted
// and sent to the client
type Notifier struct {
logger *log.Logger
sessCtx context.Context
diags chan diagContext
diagsCache map[string]map[string][]lsp.Diagnostic
closeDiagsOnce sync.Once
}

func NewNotifier(sessCtx context.Context, logger *log.Logger) *Notifier {
diags := make(chan diagContext, 50)
go notify(diags, logger)
return &Notifier{sessCtx: sessCtx, diags: diags}
n := &Notifier{logger: logger, sessCtx: sessCtx, diags: make(chan diagContext, 50), diagsCache: make(map[string]map[string][]lsp.Diagnostic)}
go n.notify()
return n
}

// Publish accepts a map of diagnostics per file and queues them for publishing
func (n *Notifier) Publish(ctx context.Context, rmDir string, diags map[string]hcl.Diagnostics, source string) {
func (n *Notifier) Publish(ctx context.Context, dir string, diags map[string][]lsp.Diagnostic, source string) {
select {
case <-n.sessCtx.Done():
n.closeDiagsOnce.Do(func() {
Expand All @@ -45,22 +47,45 @@ func (n *Notifier) Publish(ctx context.Context, rmDir string, diags map[string]h
default:
}

if source == "" {
source = "Terraform"
}

for path, ds := range diags {
n.diags <- diagContext{ctx: ctx, diags: ds, source: source, uri: lsp.DocumentURI(uri.FromPath(filepath.Join(rmDir, path)))}
for filename, ds := range diags {
n.diags <- diagContext{ctx: ctx, source: source, diags: ds, uri: lsp.DocumentURI(uri.FromPath(filepath.Join(dir, filename)))}
}
}

func notify(diags <-chan diagContext, logger *log.Logger) {
for d := range diags {
func (n *Notifier) notify() {
for d := range n.diags {
if err := jrpc2.PushNotify(d.ctx, "textDocument/publishDiagnostics", lsp.PublishDiagnosticsParams{
URI: d.uri,
Diagnostics: ilsp.HCLDiagsToLSP(d.diags, d.source),
Diagnostics: n.mergeDiags(string(d.uri), d.source, d.diags),
}); err != nil {
logger.Printf("Error pushing diagnostics: %s", err)
n.logger.Printf("Error pushing diagnostics: %s", err)
}
}
}

// mergeDiags will return all diags from all cached sources for a given uri.
// the passed diags overwrites the cached entry for the passed source key
// even if empty
func (n *Notifier) mergeDiags(uri string, source string, diags []lsp.Diagnostic) []lsp.Diagnostic {
fileDiags, ok := n.diagsCache[uri]
if !ok {
fileDiags = make(map[string][]lsp.Diagnostic)
}

fileDiags[source] = diags
n.diagsCache[uri] = fileDiags

all := []lsp.Diagnostic{}
for _, diags := range fileDiags {
all = append(all, diags...)
}
return all
}

func FromHCLMap(hclDiags map[string]hcl.Diagnostics) map[string][]lsp.Diagnostic {
diags := make(map[string][]lsp.Diagnostic, len(hclDiags))
for file, ds := range hclDiags {
diags[file] = ilsp.HCLDiagsToLSP(ds, "HCL")
}
return diags
}
10 changes: 5 additions & 5 deletions internal/langserver/diagnostics/diagnostics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"log"
"testing"

"github.com/hashicorp/hcl/v2"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
)

var discardLogger = log.New(ioutil.Discard, "", 0)
Expand All @@ -16,10 +16,10 @@ func TestDiags_Closes(t *testing.T) {
n := NewNotifier(ctx, discardLogger)

cancel()
n.Publish(context.Background(), "", map[string]hcl.Diagnostics{
n.Publish(context.Background(), "", map[string][]lsp.Diagnostic{
"test": {
{
Severity: hcl.DiagError,
Severity: lsp.SeverityError,
},
},
}, "test")
Expand All @@ -40,10 +40,10 @@ func TestPublish_DoesNotSendAfterClose(t *testing.T) {
n := NewNotifier(ctx, discardLogger)

cancel()
n.Publish(context.Background(), "", map[string]hcl.Diagnostics{
n.Publish(context.Background(), "", map[string][]lsp.Diagnostic{
"test": {
{
Severity: hcl.DiagError,
Severity: lsp.SeverityError,
},
},
}, "test")
Expand Down
3 changes: 2 additions & 1 deletion internal/langserver/handlers/did_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

lsctx "github.com/hashicorp/terraform-ls/internal/context"
"github.com/hashicorp/terraform-ls/internal/langserver/diagnostics"
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
)
Expand Down Expand Up @@ -67,7 +68,7 @@ func TextDocumentDidChange(ctx context.Context, params lsp.DidChangeTextDocument
if err != nil {
return err
}
diags.Publish(ctx, rm.Path(), rm.ParsedDiagnostics(), "HCL")
diags.Publish(ctx, rm.Path(), diagnostics.FromHCLMap(rm.ParsedDiagnostics()), "hcl")

return nil
}
3 changes: 2 additions & 1 deletion internal/langserver/handlers/did_open.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
"github.com/hashicorp/terraform-ls/internal/terraform/rootmodule"
"github.com/hashicorp/terraform-ls/internal/langserver/diagnostics"
)

func (lh *logHandler) TextDocumentDidOpen(ctx context.Context, params lsp.DidOpenTextDocumentParams) error {
Expand Down Expand Up @@ -70,7 +71,7 @@ func (lh *logHandler) TextDocumentDidOpen(ctx context.Context, params lsp.DidOpe
if err != nil {
return err
}
diags.Publish(ctx, rm.Path(), rm.ParsedDiagnostics(), "HCL")
diags.Publish(ctx, rm.Path(), diagnostics.FromHCLMap(rm.ParsedDiagnostics()), "hcl")

candidates := rmm.RootModuleCandidatesByPath(f.Dir())

Expand Down
53 changes: 53 additions & 0 deletions internal/langserver/handlers/did_save.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package handlers

import (
"context"

lsctx "github.com/hashicorp/terraform-ls/internal/context"
"github.com/hashicorp/terraform-ls/internal/langserver/diagnostics"
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
)

func (h *logHandler) TextDocumentDidSave(ctx context.Context, params lsp.DidSaveTextDocumentParams) error {
fs, err := lsctx.DocumentStorage(ctx)
if err != nil {
return err
}

rmf, err := lsctx.RootModuleFinder(ctx)
if err != nil {
return err
}

file, err := fs.GetDocument(ilsp.FileHandlerFromDocumentURI(params.TextDocument.URI))
if err != nil {
return err
}

rm, err := rmf.RootModuleByPath(file.Dir())
if err != nil {
return err
}

wasInit, err := rm.WasInitialized()
if err != nil {
h.logger.Printf("error checking if rootmodule was initialized: %s", err)
}
if !wasInit {
return nil
}

diags, err := lsctx.Diagnostics(ctx)
if err != nil {
return err
}

hclDiags, err := rm.ExecuteTerraformValidate(ctx)
if err != nil {
return err
}
diags.Publish(ctx, rm.Path(), diagnostics.FromHCLMap(hclDiags), "validate")

return nil
}
10 changes: 10 additions & 0 deletions internal/langserver/handlers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,16 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {
ctx = lsctx.WithDocumentStorage(ctx, svc.fs)
return handle(ctx, req, TextDocumentDidClose)
},
"textDocument/didSave": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
return nil, err
}
ctx = lsctx.WithDiagnostics(ctx, diags)
ctx = lsctx.WithDocumentStorage(ctx, svc.fs)
ctx = lsctx.WithRootModuleFinder(ctx, svc.modMgr)
return handle(ctx, req, lh.TextDocumentDidSave)
},
"textDocument/documentSymbol": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions internal/terraform/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ func (e *Executor) Format(ctx context.Context, input []byte) ([]byte, error) {
return buf.Bytes(), e.contextfulError(ctx, "Format", err)
}

func (e *Executor) Validate(ctx context.Context) ([]tfexec.Diagnostic, error) {
ctx, cancel := e.withTimeout(ctx)
defer cancel()
err := e.setLogPath("Validate")
if err != nil {
return []tfexec.Diagnostic{}, err
}

validation, err := e.tf.Validate(ctx)

return validation.Diagnostics, e.contextfulError(ctx, "Validate", err)
}

func (e *Executor) Version(ctx context.Context) (*version.Version, map[string]*version.Version, error) {
ctx, cancel := e.withTimeout(ctx)
defer cancel()
Expand Down
23 changes: 23 additions & 0 deletions internal/terraform/exec/mock/executor.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/exec/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ type TerraformExecutor interface {
Init(ctx context.Context, opts ...tfexec.InitOption) error
Format(ctx context.Context, input []byte) ([]byte, error)
Version(ctx context.Context) (*version.Version, map[string]*version.Version, error)
Validate(ctx context.Context) ([]tfexec.Diagnostic, error)
ProviderSchemas(ctx context.Context) (*tfjson.ProviderSchemas, error)
}
Loading

0 comments on commit 42af1b9

Please sign in to comment.