mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-16 11:42:58 -06:00
39bc5e825b
Sensitive values may not be used in outputs which are not also marked as sensitive. This includes values nested within complex structures. Note that sensitive values are unmarked before writing to state. This means that sensitive values used in module outputs will have the sensitive mark removed. At the moment, we have not implemented sensitivity propagation from module outputs back to value marks. This commit also reworks the tests for NodeApplyableOutput to cover more existing behaviour, as well as this change.
162 lines
4.8 KiB
Go
162 lines
4.8 KiB
Go
package terraform
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestNodeApplyableOutputExecute_knownValue(t *testing.T) {
|
|
ctx := new(MockEvalContext)
|
|
ctx.StateState = states.NewState().SyncWrapper()
|
|
ctx.RefreshStateState = states.NewState().SyncWrapper()
|
|
|
|
config := &configs.Output{Name: "map-output"}
|
|
addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
|
|
node := &NodeApplyableOutput{Config: config, Addr: addr}
|
|
val := cty.MapVal(map[string]cty.Value{
|
|
"a": cty.StringVal("b"),
|
|
})
|
|
ctx.EvaluateExprResult = val
|
|
|
|
err := node.Execute(ctx, walkApply)
|
|
if err != nil {
|
|
t.Fatalf("unexpected execute error: %s", err)
|
|
}
|
|
|
|
outputVal := ctx.StateState.OutputValue(addr)
|
|
if got, want := outputVal.Value, val; !got.RawEquals(want) {
|
|
t.Errorf("wrong output value in state\n got: %#v\nwant: %#v", got, want)
|
|
}
|
|
|
|
if !ctx.RefreshStateCalled {
|
|
t.Fatal("should have called RefreshState, but didn't")
|
|
}
|
|
refreshOutputVal := ctx.RefreshStateState.OutputValue(addr)
|
|
if got, want := refreshOutputVal.Value, val; !got.RawEquals(want) {
|
|
t.Fatalf("wrong output value in refresh state\n got: %#v\nwant: %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestNodeApplyableOutputExecute_noState(t *testing.T) {
|
|
ctx := new(MockEvalContext)
|
|
|
|
config := &configs.Output{Name: "map-output"}
|
|
addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
|
|
node := &NodeApplyableOutput{Config: config, Addr: addr}
|
|
val := cty.MapVal(map[string]cty.Value{
|
|
"a": cty.StringVal("b"),
|
|
})
|
|
ctx.EvaluateExprResult = val
|
|
|
|
err := node.Execute(ctx, walkApply)
|
|
if err != nil {
|
|
t.Fatalf("unexpected execute error: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestNodeApplyableOutputExecute_invalidDependsOn(t *testing.T) {
|
|
ctx := new(MockEvalContext)
|
|
ctx.StateState = states.NewState().SyncWrapper()
|
|
|
|
config := &configs.Output{
|
|
Name: "map-output",
|
|
DependsOn: []hcl.Traversal{
|
|
{
|
|
hcl.TraverseRoot{Name: "test_instance"},
|
|
hcl.TraverseAttr{Name: "foo"},
|
|
hcl.TraverseAttr{Name: "bar"},
|
|
},
|
|
},
|
|
}
|
|
addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
|
|
node := &NodeApplyableOutput{Config: config, Addr: addr}
|
|
val := cty.MapVal(map[string]cty.Value{
|
|
"a": cty.StringVal("b"),
|
|
})
|
|
ctx.EvaluateExprResult = val
|
|
|
|
err := node.Execute(ctx, walkApply)
|
|
if err == nil {
|
|
t.Fatal("expected execute error, but there was none")
|
|
}
|
|
if got, want := err.Error(), "Invalid depends_on reference"; !strings.Contains(got, want) {
|
|
t.Errorf("expected error to include %q, but was: %s", want, got)
|
|
}
|
|
}
|
|
|
|
func TestNodeApplyableOutputExecute_sensitiveValueNotOutput(t *testing.T) {
|
|
ctx := new(MockEvalContext)
|
|
ctx.StateState = states.NewState().SyncWrapper()
|
|
|
|
config := &configs.Output{Name: "map-output"}
|
|
addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
|
|
node := &NodeApplyableOutput{Config: config, Addr: addr}
|
|
val := cty.MapVal(map[string]cty.Value{
|
|
"a": cty.StringVal("b").Mark("sensitive"),
|
|
})
|
|
ctx.EvaluateExprResult = val
|
|
|
|
err := node.Execute(ctx, walkApply)
|
|
if err == nil {
|
|
t.Fatal("expected execute error, but there was none")
|
|
}
|
|
if got, want := err.Error(), "Output refers to sensitive values"; !strings.Contains(got, want) {
|
|
t.Errorf("expected error to include %q, but was: %s", want, got)
|
|
}
|
|
}
|
|
|
|
func TestNodeApplyableOutputExecute_sensitiveValueAndOutput(t *testing.T) {
|
|
ctx := new(MockEvalContext)
|
|
ctx.StateState = states.NewState().SyncWrapper()
|
|
|
|
config := &configs.Output{
|
|
Name: "map-output",
|
|
Sensitive: true,
|
|
}
|
|
addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
|
|
node := &NodeApplyableOutput{Config: config, Addr: addr}
|
|
val := cty.MapVal(map[string]cty.Value{
|
|
"a": cty.StringVal("b").Mark("sensitive"),
|
|
})
|
|
ctx.EvaluateExprResult = val
|
|
|
|
err := node.Execute(ctx, walkApply)
|
|
if err != nil {
|
|
t.Fatalf("unexpected execute error: %s", err)
|
|
}
|
|
|
|
// Unmarked value should be stored in state
|
|
outputVal := ctx.StateState.OutputValue(addr)
|
|
want, _ := val.UnmarkDeep()
|
|
if got := outputVal.Value; !got.RawEquals(want) {
|
|
t.Errorf("wrong output value in state\n got: %#v\nwant: %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestNodeDestroyableOutputExecute(t *testing.T) {
|
|
outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)
|
|
|
|
state := states.NewState()
|
|
state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false)
|
|
state.OutputValue(outputAddr)
|
|
|
|
ctx := &MockEvalContext{
|
|
StateState: state.SyncWrapper(),
|
|
}
|
|
node := NodeDestroyableOutput{Addr: outputAddr}
|
|
|
|
err := node.Execute(ctx, walkApply)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err.Error())
|
|
}
|
|
if state.OutputValue(outputAddr) != nil {
|
|
t.Fatal("Unexpected outputs in state after removal")
|
|
}
|
|
}
|