Skip to content

Commit

Permalink
feat: parse .terraform-version files for stacks dirs
Browse files Browse the repository at this point in the history
  • Loading branch information
ansgarm committed Jul 2, 2024
1 parent c47b2e5 commit bcfdcef
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 3 deletions.
3 changes: 2 additions & 1 deletion internal/features/stacks/decoder/stacks_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error)
}

// get terrafom version from statereader and use that to get the schema
// -> record.TerraformVersion is available now (if already set hehe)

// TODO: This only provides tfstacks schema. There is also tfdeploy schema
// TODO: this should only work for terraform 1.8 and above
Expand Down Expand Up @@ -68,7 +69,7 @@ func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error)
for name, f := range record.ParsedStackFiles {
pathCtx.Files[name.String()] = f
}

for name, f := range record.ParsedDeployFiles {
pathCtx.Files[name.String()] = f
}
Expand Down
14 changes: 14 additions & 0 deletions internal/features/stacks/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,20 @@ func (f *StacksFeature) decodeStacks(ctx context.Context, dir document.DirHandle
}
ids = append(ids, parseId)

tfVersion, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
Dir: dir,
Func: func(ctx context.Context) error {
return jobs.LoadTerraformVersion(ctx, f.fs, f.Store, path)
},
Type: operation.OpTypeLoadModuleMetadata.String(),
DependsOn: job.IDs{parseId},
IgnoreState: ignoreState,
})
if err != nil {
return ids, err
}
ids = append(ids, tfVersion)

// TODO: Implement the following functions where appropriate to stacks
// Future: LoadModuleMetadata(ctx, f.Store, path)
// Future: decodeDeclaredModuleCalls(ctx, dir, ignoreState)
Expand Down
50 changes: 50 additions & 0 deletions internal/features/stacks/jobs/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package jobs

import (
"context"
"path/filepath"
"strings"

"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-ls/internal/document"
"github.com/hashicorp/terraform-ls/internal/features/stacks/state"
"github.com/hashicorp/terraform-ls/internal/job"
"github.com/hashicorp/terraform-ls/internal/terraform/module/operation"
)

// TODO: find a place to register a watcher for this .terraform-version file (put this in a follow-up issue), for now reloading the window is okay enough
// https://github.com/hashicorp/terraform-ls/blob/c47b2e51a0ca08628a596eca8d5cd11d005b7d87/internal/langserver/handlers/initialized.go#L25

// LoadTerraformVersion loads the terraform version from the .terraform-version
// file in the stack directory.
func LoadTerraformVersion(ctx context.Context, fs ReadOnlyFS, stackStore *state.StackStore, stackPath string) error {
stackRecord, err := stackStore.StackRecordByPath(stackPath)
if err != nil {
return err
}

// Avoid parsing if it is already in progress or already known
if stackRecord.TerraformVersionState != operation.OpStateUnknown && !job.IgnoreState(ctx) {
return job.StateNotChangedErr{Dir: document.DirHandleFromPath(stackPath)}
}

stackStore.SetTerraformVersionState(stackPath, operation.OpStateLoading)

// read version file
v, err := fs.ReadFile(filepath.Join(stackPath, ".terraform-version"))
if err != nil {
stackStore.SetTerraformVersionError(stackPath, err)
return err
}

// parse version
version, err := version.NewVersion(strings.TrimSpace(string(v)))
if err != nil {
stackStore.SetTerraformVersionError(stackPath, err)
return err
}

stackStore.SetTerraformVersion(stackPath, version)

return nil
}
5 changes: 5 additions & 0 deletions internal/features/stacks/state/stacks_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package state

import (
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/features/stacks/ast"
globalAst "github.com/hashicorp/terraform-ls/internal/terraform/ast"
Expand All @@ -28,6 +29,10 @@ type StackRecord struct {
DeployParsingErr error
DeployDiagnostics ast.SourceDeployDiags
DeployDiagnosticsState globalAst.DiagnosticSourceState

TerraformVersion *version.Version
TerraformVersionErr error
TerraformVersionState operation.OpState
}

func (m *StackRecord) Path() string {
Expand Down
61 changes: 61 additions & 0 deletions internal/features/stacks/state/stacks_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"

"github.com/hashicorp/go-memdb"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-ls/internal/features/stacks/ast"
globalState "github.com/hashicorp/terraform-ls/internal/state"
globalAst "github.com/hashicorp/terraform-ls/internal/terraform/ast"
Expand All @@ -17,6 +18,11 @@ type StackStore struct {
db *memdb.MemDB
tableName string
logger *log.Logger

// MaxStackNesting represents how many nesting levels we'd attempt
// to parse provider requirements before returning error.
MaxStackNesting int
changeStore *globalState.ChangeStore
}

func (s *StackStore) SetLogger(logger *log.Logger) {
Expand Down Expand Up @@ -273,6 +279,61 @@ func (s *StackStore) UpdateDeployDiagnostics(path string, source globalAst.Diagn
return nil
}

func (s *StackStore) setTerraformVersionWithChangeNotification(path string, version *version.Version, vErr error, state operation.OpState) error {
txn := s.db.Txn(true)
defer txn.Abort()

oldStack, err := stackByPath(txn, path)
if err != nil {
return err
}
stack := oldStack.Copy()

stack.TerraformVersion = version
stack.TerraformVersionErr = vErr
stack.TerraformVersionState = state

err = txn.Insert(s.tableName, stack)
if err != nil {
return err
}

// err = s.queueStacksChange(oldStack, stack)
// if err != nil {
// return err
// }

txn.Commit()
return nil
}

func (s *StackStore) SetTerraformVersion(path string, version *version.Version) error {
return s.setTerraformVersionWithChangeNotification(path, version, nil, operation.OpStateLoaded)
}

func (s *StackStore) SetTerraformVersionError(path string, vErr error) error {
return s.setTerraformVersionWithChangeNotification(path, nil, vErr, operation.OpStateLoaded)
}

func (s *StackStore) SetTerraformVersionState(path string, state operation.OpState) error {
txn := s.db.Txn(true)
defer txn.Abort()

stack, err := stackCopyByPath(txn, path)
if err != nil {
return err
}

stack.TerraformVersionState = state
err = txn.Insert(s.tableName, stack)
if err != nil {
return err
}

txn.Commit()
return nil
}

func (s *StackStore) add(txn *memdb.Txn, stackPath string) error {
// TODO: Introduce Exists method to Txn?
obj, err := txn.First(s.tableName, "id", stackPath)
Expand Down
5 changes: 3 additions & 2 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 @@ -38,4 +38,5 @@ const (
OpTypeParseStacks
OpTypeParseStacksConfiguration
OpTypeParseDeployConfiguration
OpTypeLoadTerraformVersion
)

0 comments on commit bcfdcef

Please sign in to comment.