Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading