Backport/v1.8/1826 (#1829)

Signed-off-by: James Humphries <james@james-humphries.co.uk>
Co-authored-by: James Humphries <james@james-humphries.co.uk>
This commit is contained in:
Christian Mesh 2024-07-18 08:11:39 -04:00 committed by GitHub
parent f668c48ffd
commit 0b2384a3ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 129 additions and 97 deletions

View File

@ -280,7 +280,7 @@ func (b *Local) localRunForPlanFile(op *backend.Operation, pf *planfile.Reader,
return variable.Default, nil return variable.Default, nil
} }
parsed, parsedErr := v.Decode(variable.Type) parsed, parsedErr := v.Decode(cty.DynamicPseudoType)
if parsedErr != nil { if parsedErr != nil {
diags = diags.Append(&hcl.Diagnostic{ diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,

View File

@ -6,6 +6,7 @@
package e2etest package e2etest
import ( import (
"fmt"
"path/filepath" "path/filepath"
"testing" "testing"
@ -15,121 +16,129 @@ import (
// This is an e2e test as it relies on very specific configuration // This is an e2e test as it relies on very specific configuration
// within the meta object that is currently very hard to mock out. // within the meta object that is currently very hard to mock out.
func TestStaticPlanVariables(t *testing.T) { func TestStaticPlanVariables(t *testing.T) {
fixturePath := filepath.Join("testdata", "static_plan_variables") fixtures := []string{
tf := e2e.NewBinary(t, tofuBin, fixturePath) "static_plan_variables",
"static_plan_typed_variables",
run := func(args ...string) tofuResult {
stdout, stderr, err := tf.Run(args...)
return tofuResult{t, stdout, stderr, err}
} }
for _, fixture := range fixtures {
t.Run(fmt.Sprintf("TestStaticPlanVariables/%s", fixture), func(t *testing.T) {
fixturePath := filepath.Join("testdata", fixture)
tf := e2e.NewBinary(t, tofuBin, fixturePath)
statePath := "custom.tfstate" run := func(args ...string) tofuResult {
stateVar := "-var=state_path=" + statePath stdout, stderr, err := tf.Run(args...)
modVar := "-var=src=./mod" return tofuResult{t, stdout, stderr, err}
planfile := "static.plan" }
modErr := "module.mod.source depends on var.src which is not available" statePath := "custom.tfstate"
backendErr := "backend.local depends on var.state_path which is not available" stateVar := "-var=state_path=" + statePath
modVar := "-var=src=./mod"
planfile := "static.plan"
// Init modErr := "module.mod.source depends on var.src which is not available"
run("init").Failure().StderrContains(modErr) backendErr := "backend.local depends on var.state_path which is not available"
run("init", stateVar, modVar).Success()
// Get // Init
run("get").Failure().StderrContains(modErr) run("init").Failure().StderrContains(modErr)
run("get", stateVar, modVar).Success() run("init", stateVar, modVar).Success()
// Validate // Get
run("validate").Failure().StderrContains(modErr) run("get").Failure().StderrContains(modErr)
run("validate", stateVar, modVar).Success() run("get", stateVar, modVar).Success()
// Providers // Validate
run("providers").Failure().StderrContains(modErr) run("validate").Failure().StderrContains(modErr)
run("providers", stateVar, modVar).Success() run("validate", stateVar, modVar).Success()
run("providers", "lock").Failure().StderrContains(modErr)
run("providers", "lock", stateVar, modVar).Success()
run("providers", "mirror", "./tempproviders").Failure().StderrContains(modErr)
run("providers", "mirror", stateVar, modVar, "./tempproviders").Failure().StderrContains("Could not scan the output directory to get package metadata for the JSON")
run("providers", "schema", "-json").Failure().StderrContains(backendErr)
run("providers", "schema", "-json", stateVar, modVar).Success()
// Check console init (early exits due to stdin setup) // Providers
run("console").Failure().StderrContains(backendErr) run("providers").Failure().StderrContains(modErr)
run("console", stateVar, modVar).Success() run("providers", stateVar, modVar).Success()
run("providers", "lock").Failure().StderrContains(modErr)
run("providers", "lock", stateVar, modVar).Success()
run("providers", "mirror", "./tempproviders").Failure().StderrContains(modErr)
run("providers", "mirror", stateVar, modVar, "./tempproviders").Failure().StderrContains("Could not scan the output directory to get package metadata for the JSON")
run("providers", "schema", "-json").Failure().StderrContains(backendErr)
run("providers", "schema", "-json", stateVar, modVar).Success()
// Check graph (without plan) // Check console init (early exits due to stdin setup)
run("graph").Failure().StderrContains(backendErr) run("console").Failure().StderrContains(backendErr)
run("graph", stateVar, modVar).Success() run("console", stateVar, modVar).Success()
// Plan with static variable // Check graph (without plan)
run("plan", stateVar, modVar, "-out="+planfile).Success() run("graph").Failure().StderrContains(backendErr)
run("graph", stateVar, modVar).Success()
// Show plan without static variable (embedded) // Plan with static variable
run("show", planfile).Success() run("plan", stateVar, modVar, "-out="+planfile).Success()
// Check graph (without plan) // Show plan without static variable (embedded)
run("graph", "-plan="+planfile).Success() run("show", planfile).Success()
// Apply plan without static variable (embedded) // Check graph (without plan)
run("apply", planfile).Success() run("graph", "-plan="+planfile).Success()
// Show State // Apply plan without static variable (embedded)
run("show", statePath).Failure().StderrContains(modErr) run("apply", planfile).Success()
run("show", stateVar, modVar, statePath).Success().Contains(`out = "placeholder"`)
// Force Unlock // Show State
run("force-unlock", "ident").Failure().StderrContains(backendErr) run("show", statePath).Failure().StderrContains(modErr)
run("force-unlock", stateVar, modVar, "ident").Failure().StderrContains("Local state cannot be unlocked by another process") run("show", stateVar, modVar, statePath).Success().Contains(`out = "placeholder"`)
// Output values // Force Unlock
run("output").Failure().StderrContains(backendErr) run("force-unlock", "ident").Failure().StderrContains(backendErr)
run("output", stateVar, modVar).Success().Contains(`out = "placeholder"`) run("force-unlock", stateVar, modVar, "ident").Failure().StderrContains("Local state cannot be unlocked by another process")
// Refresh // Output values
run("refresh").Failure().StderrContains(backendErr) run("output").Failure().StderrContains(backendErr)
run("refresh", stateVar, modVar).Success().Contains("There are currently no remote objects tracked in the state") run("output", stateVar, modVar).Success().Contains(`out = "placeholder"`)
// Import // Refresh
run("import", "resource.addr", "id").Failure().StderrContains(modErr) run("refresh").Failure().StderrContains(backendErr)
run("import", stateVar, modVar, "resource.addr", "id").Failure().StderrContains("Before importing this resource, please create its configuration in the root module.") run("refresh", stateVar, modVar).Success().Contains("There are currently no remote objects tracked in the state")
// Taint // Import
run("taint", "resource.addr").Failure().StderrContains(modErr) run("import", "resource.addr", "id").Failure().StderrContains(modErr)
run("taint", stateVar, modVar, "resource.addr").Failure().StderrContains("There is no resource instance in the state with the address resource.addr.") run("import", stateVar, modVar, "resource.addr", "id").Failure().StderrContains("Before importing this resource, please create its configuration in the root module.")
run("untaint", "resource.addr").Failure().StderrContains(backendErr)
run("untaint", stateVar, modVar, "resource.addr").Failure().StderrContains("There is no resource instance in the state with the address resource.addr.")
// State // Taint
run("state", "list").Failure().StderrContains(backendErr) run("taint", "resource.addr").Failure().StderrContains(modErr)
run("state", "list", stateVar, modVar).Success() run("taint", stateVar, modVar, "resource.addr").Failure().StderrContains("There is no resource instance in the state with the address resource.addr.")
run("state", "mv", "foo.bar", "foo.baz").Failure().StderrContains(modErr) run("untaint", "resource.addr").Failure().StderrContains(backendErr)
run("state", "mv", stateVar, modVar, "foo.bar", "foo.baz").Failure().StderrContains("Cannot move foo.bar: does not match anything in the current state.") run("untaint", stateVar, modVar, "resource.addr").Failure().StderrContains("There is no resource instance in the state with the address resource.addr.")
run("state", "pull").Failure().StderrContains(modErr)
run("state", "pull", stateVar, modVar).Success().Contains(`"outputs":{"out":{"value":"placeholder","type":"string"}}`)
run("state", "push", statePath).Failure().StderrContains(modErr)
run("state", "push", stateVar, modVar, statePath).Success()
run("state", "replace-provider", "foo", "bar").Failure().StderrContains(modErr)
run("state", "replace-provider", stateVar, modVar, "foo", "bar").Success().Contains("No matching resources found.")
run("state", "rm", "foo.bar").Failure().StderrContains(modErr)
run("state", "rm", stateVar, modVar, "foo.bar").Failure().StderrContains("No matching objects found.")
run("state", "show", "out").Failure().StderrContains(backendErr)
run("state", "show", stateVar, modVar, "invalid.resource").Failure().StderrContains("No instance found for the given address!")
// Workspace // State
run("workspace", "list").Failure().StderrContains(backendErr) run("state", "list").Failure().StderrContains(backendErr)
run("workspace", "list", stateVar, modVar).Success().Contains(`default`) run("state", "list", stateVar, modVar).Success()
run("workspace", "new", "foo").Failure().StderrContains(backendErr) run("state", "mv", "foo.bar", "foo.baz").Failure().StderrContains(modErr)
run("workspace", "new", stateVar, modVar, "foo").Success().Contains(`foo`) run("state", "mv", stateVar, modVar, "foo.bar", "foo.baz").Failure().StderrContains("Cannot move foo.bar: does not match anything in the current state.")
run("workspace", "select", "default").Failure().StderrContains(backendErr) run("state", "pull").Failure().StderrContains(modErr)
run("workspace", "select", stateVar, modVar, "default").Success().Contains(`default`) run("state", "pull", stateVar, modVar).Success().Contains(`"outputs":{"out":{"value":"placeholder","type":"string"}}`)
run("workspace", "delete", "foo").Failure().StderrContains(backendErr) run("state", "push", statePath).Failure().StderrContains(modErr)
run("workspace", "delete", stateVar, modVar, "foo").Success().Contains(`foo`) run("state", "push", stateVar, modVar, statePath).Success()
run("state", "replace-provider", "foo", "bar").Failure().StderrContains(modErr)
run("state", "replace-provider", stateVar, modVar, "foo", "bar").Success().Contains("No matching resources found.")
run("state", "rm", "foo.bar").Failure().StderrContains(modErr)
run("state", "rm", stateVar, modVar, "foo.bar").Failure().StderrContains("No matching objects found.")
run("state", "show", "out").Failure().StderrContains(backendErr)
run("state", "show", stateVar, modVar, "invalid.resource").Failure().StderrContains("No instance found for the given address!")
// Test // Workspace
run("test").Failure().StderrContains(modErr) run("workspace", "list").Failure().StderrContains(backendErr)
run("test", stateVar, modVar).Success().Contains(`Success!`) run("workspace", "list", stateVar, modVar).Success().Contains(`default`)
run("workspace", "new", "foo").Failure().StderrContains(backendErr)
run("workspace", "new", stateVar, modVar, "foo").Success().Contains(`foo`)
run("workspace", "select", "default").Failure().StderrContains(backendErr)
run("workspace", "select", stateVar, modVar, "default").Success().Contains(`default`)
run("workspace", "delete", "foo").Failure().StderrContains(backendErr)
run("workspace", "delete", stateVar, modVar, "foo").Success().Contains(`foo`)
// Destroy // Test
run("destroy", "-auto-approve").Failure().StderrContains(backendErr) run("test").Failure().StderrContains(modErr)
run("destroy", stateVar, modVar, "-auto-approve").Success().Contains("You can apply this plan to save these new output values") run("test", stateVar, modVar).Success().Contains(`Success!`)
// Destroy
run("destroy", "-auto-approve").Failure().StderrContains(backendErr)
run("destroy", stateVar, modVar, "-auto-approve").Success().Contains("You can apply this plan to save these new output values")
})
}
} }

View File

@ -0,0 +1,19 @@
variable "state_path" {}
variable "src" {
type = string
}
terraform {
backend "local" {
path = var.state_path
}
}
module "mod" {
source = var.src
}
output "out" {
value = module.mod.out
}

View File

@ -0,0 +1,3 @@
output "out" {
value = "placeholder"
}

View File

@ -13,6 +13,8 @@ import (
"strings" "strings"
"github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/backend" "github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/cloud" "github.com/opentofu/opentofu/internal/cloud"
"github.com/opentofu/opentofu/internal/cloud/cloudplan" "github.com/opentofu/opentofu/internal/cloud/cloudplan"
@ -27,7 +29,6 @@ import (
"github.com/opentofu/opentofu/internal/tfdiags" "github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tofu" "github.com/opentofu/opentofu/internal/tofu"
"github.com/opentofu/opentofu/internal/tofumigrate" "github.com/opentofu/opentofu/internal/tofumigrate"
"github.com/zclconf/go-cty/cty"
) )
// Many of the methods we get data from can emit special error types if they're // Many of the methods we get data from can emit special error types if they're
@ -382,7 +383,7 @@ func getDataFromPlanfileReader(planReader *planfile.Reader, rootCall configs.Sta
return variable.Default, nil return variable.Default, nil
} }
parsed, parsedErr := v.Decode(variable.Type) parsed, parsedErr := v.Decode(cty.DynamicPseudoType)
if parsedErr != nil { if parsedErr != nil {
diags = diags.Append(&hcl.Diagnostic{ diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,