Skip to content

Commit

Permalink
deferred actions: add common functionality for evaluating unexpanded …
Browse files Browse the repository at this point in the history
…and expanded modules (#35009)

* deferred actions: add common functionality for evaluating unexpanded and expanded modules

* move evaluate_state back into original file
  • Loading branch information
liamcervante authored May 13, 2024
1 parent bba161c commit e40bb9b
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 220 deletions.
10 changes: 8 additions & 2 deletions internal/terraform/eval_context_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,10 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source
switch scope := ctx.scope.(type) {
case evalContextModuleInstance:
data := &evaluationStateData{
Evaluator: ctx.Evaluator,
evaluationData: &evaluationData{
Evaluator: ctx.Evaluator,
Module: scope.Addr.Module(),
},
ModulePath: scope.Addr,
InstanceKeyData: keyData,
Operation: ctx.Evaluator.Operation,
Expand All @@ -463,7 +466,10 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source
return evalScope
case evalContextPartialExpandedModule:
data := &evaluationPlaceholderData{
Evaluator: ctx.Evaluator,
evaluationData: &evaluationData{
Evaluator: ctx.Evaluator,
Module: scope.Addr.Module(),
},
ModulePath: scope.Addr,
CountAvailable: keyData.CountIndex != cty.NilVal,
EachAvailable: keyData.EachKey != cty.NilVal,
Expand Down
159 changes: 1 addition & 158 deletions internal/terraform/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package terraform
import (
"fmt"
"log"
"os"
"path/filepath"
"time"

"github.com/hashicorp/hcl/v2"
Expand Down Expand Up @@ -98,7 +96,7 @@ func (e *Evaluator) Scope(data lang.Data, self addrs.Referenceable, source addrs
// evaluationStateData is an implementation of lang.Data that resolves
// references primarily (but not exclusively) using information from a State.
type evaluationStateData struct {
Evaluator *Evaluator
*evaluationData

// ModulePath is the path through the dynamic module tree to the module
// that references will be resolved relative to.
Expand Down Expand Up @@ -482,75 +480,6 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc
}
}

func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
switch addr.Name {

case "cwd":
var err error
var wd string
if d.Evaluator.Meta != nil {
// Meta is always non-nil in the normal case, but some test cases
// are not so realistic.
wd = d.Evaluator.Meta.OriginalWorkingDir
}
if wd == "" {
wd, err = os.Getwd()
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Failed to get working directory`,
Detail: fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
Subject: rng.ToHCL().Ptr(),
})
return cty.DynamicVal, diags
}
}
// The current working directory should always be absolute, whether we
// just looked it up or whether we were relying on ContextMeta's
// (possibly non-normalized) path.
wd, err = filepath.Abs(wd)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Failed to get working directory`,
Detail: fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
Subject: rng.ToHCL().Ptr(),
})
return cty.DynamicVal, diags
}

return cty.StringVal(filepath.ToSlash(wd)), diags

case "module":
moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
if moduleConfig == nil {
// should never happen, since we can't be evaluating in a module
// that wasn't mentioned in configuration.
panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.ModulePath))
}
sourceDir := moduleConfig.Module.SourceDir
return cty.StringVal(filepath.ToSlash(sourceDir)), diags

case "root":
sourceDir := d.Evaluator.Config.Module.SourceDir
return cty.StringVal(filepath.ToSlash(sourceDir)), diags

default:
suggestion := didyoumean.NameSuggestion(addr.Name, []string{"cwd", "module", "root"})
if suggestion != "" {
suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
}
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Invalid "path" attribute`,
Detail: fmt.Sprintf(`The "path" object does not have an attribute named %q.%s`, addr.Name, suggestion),
Subject: rng.ToHCL().Ptr(),
})
return cty.DynamicVal, diags
}
}

func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
// First we'll consult the configuration to see if an resource of this
Expand Down Expand Up @@ -840,68 +769,6 @@ func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAdd
return schema
}

func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

if d.Evaluator.Meta == nil || d.Evaluator.Meta.Env == "" {
// The absense of an "env" (really: workspace) name suggests that
// we're running in a non-workspace context, such as in a component
// of a stack. terraform.workspace -- and the terraform symbol in
// general -- is a legacy thing from workspaces mode that isn't
// carried forward to stacks, because stack configurations can instead
// vary their behavior based on input variables provided in the
// deployment configuration.
switch addr.Name {
case "workspace":
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Invalid reference`,
Detail: `The terraform.workspace attribute is only available for modules used in Terraform workspaces. Use input variables instead to create variations between different instances of this module.`,
Subject: rng.ToHCL().Ptr(),
})
default:
// A more generic error for any other attribute name, since no
// others are valid anyway but it would be confusing to mention
// terraform.workspace here.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Invalid reference`,
Detail: `The "terraform" object is only available for modules used in Terraform workspaces.`,
Subject: rng.ToHCL().Ptr(),
})
}
return cty.DynamicVal, diags
}

switch addr.Name {

case "workspace":
workspaceName := d.Evaluator.Meta.Env
return cty.StringVal(workspaceName), diags

case "env":
// Prior to Terraform 0.12 there was an attribute "env", which was
// an alias name for "workspace". This was deprecated and is now
// removed.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Invalid "terraform" attribute`,
Detail: `The terraform.env attribute was deprecated in v0.10 and removed in v0.12. The "state environment" concept was renamed to "workspace" in v0.12, and so the workspace name can now be accessed using the terraform.workspace attribute.`,
Subject: rng.ToHCL().Ptr(),
})
return cty.DynamicVal, diags

default:
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Invalid "terraform" attribute`,
Detail: fmt.Sprintf(`The "terraform" object does not have an attribute named %q. The only supported attribute is terraform.workspace, the name of the currently-selected workspace.`, addr.Name),
Subject: rng.ToHCL().Ptr(),
})
return cty.DynamicVal, diags
}
}

func (d *evaluationStateData) GetOutput(addr addrs.OutputValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

Expand Down Expand Up @@ -957,30 +824,6 @@ func (d *evaluationStateData) GetOutput(addr addrs.OutputValue, rng tfdiags.Sour
return val, diags
}

func (d *evaluationStateData) GetCheckBlock(addr addrs.Check, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
// For now, check blocks don't contain any meaningful data and can only
// be referenced from the testing scope within an expect_failures attribute.
//
// We've added them into the scope explicitly since they are referencable,
// but we'll actually just return an error message saying they can't be
// referenced in this context.
var diags tfdiags.Diagnostics
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Reference to \"check\" in invalid context",
Detail: "The \"check\" object can only be referenced from an \"expect_failures\" attribute within a Terraform testing \"run\" block.",
Subject: rng.ToHCL().Ptr(),
})
return cty.NilVal, diags
}

func (d *evaluationStateData) GetRunBlock(run addrs.Run, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
// We should not get here because any scope that has an [evaluationStateData]
// as its Data should have a reference parser that doesn't accept addrs.Run
// addresses.
panic("GetRunBlock called on non-test evaluation dataset")
}

// moduleDisplayAddr returns a string describing the given module instance
// address that is appropriate for returning to users in situations where the
// root module is possible. Specifically, it returns "the root module" if the
Expand Down
Loading

0 comments on commit e40bb9b

Please sign in to comment.