mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
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:
parent
f668c48ffd
commit
0b2384a3ef
@ -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,
|
||||||
|
@ -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")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
internal/command/e2etest/testdata/static_plan_typed_variables/main.tf
vendored
Normal file
19
internal/command/e2etest/testdata/static_plan_typed_variables/main.tf
vendored
Normal 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
|
||||||
|
}
|
3
internal/command/e2etest/testdata/static_plan_typed_variables/mod/mod.tf
vendored
Normal file
3
internal/command/e2etest/testdata/static_plan_typed_variables/mod/mod.tf
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
output "out" {
|
||||||
|
value = "placeholder"
|
||||||
|
}
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user