mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Ephemeral todos handling (#3177)
Signed-off-by: Andrei Ciobanu <andrei.ciobanu@opentofu.org> Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
committed by
Christian Mesh
parent
ccfeb83889
commit
1bab9aff46
@@ -241,10 +241,7 @@ func TestPrimaryChdirOption(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This test is checking the workflow of the ephemeral resources.
|
// This test is checking the workflow of the ephemeral resources.
|
||||||
// Check also the configuration files for comments. The idea is that at the time of
|
// Check also the configuration files for comments.
|
||||||
// writing, the configuration was done in such a way to fail later when the
|
|
||||||
// marks will be introduced for ephemeral values. Therefore, this test will
|
|
||||||
// fail later and will require adjustments.
|
|
||||||
//
|
//
|
||||||
// We want to validate that the plan file, state file and the output contain
|
// We want to validate that the plan file, state file and the output contain
|
||||||
// only the things that are needed:
|
// only the things that are needed:
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
// should generally be suitable for display to an end-user anyway.
|
// should generally be suitable for display to an end-user anyway.
|
||||||
//
|
//
|
||||||
// This function will panic if the given value is not of an object type.
|
// This function will panic if the given value is not of an object type.
|
||||||
// TODO ephemeral - check that ephemeral is not going to impact this
|
|
||||||
func ObjectValueID(obj cty.Value) (k, v string) {
|
func ObjectValueID(obj cty.Value) (k, v string) {
|
||||||
if obj.IsNull() || !obj.IsKnown() {
|
if obj.IsNull() || !obj.IsKnown() {
|
||||||
return "", ""
|
return "", ""
|
||||||
@@ -37,7 +36,7 @@ func ObjectValueID(obj cty.Value) (k, v string) {
|
|||||||
|
|
||||||
case atys["id"] == cty.String:
|
case atys["id"] == cty.String:
|
||||||
v := obj.GetAttr("id")
|
v := obj.GetAttr("id")
|
||||||
if v.HasMark(marks.Sensitive) {
|
if v.HasMark(marks.Sensitive) || v.HasMark(marks.Ephemeral) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v, _ = v.Unmark()
|
v, _ = v.Unmark()
|
||||||
@@ -50,7 +49,7 @@ func ObjectValueID(obj cty.Value) (k, v string) {
|
|||||||
// "name" isn't always globally unique, but if there isn't also an
|
// "name" isn't always globally unique, but if there isn't also an
|
||||||
// "id" then it _often_ is, in practice.
|
// "id" then it _often_ is, in practice.
|
||||||
v := obj.GetAttr("name")
|
v := obj.GetAttr("name")
|
||||||
if v.HasMark(marks.Sensitive) {
|
if v.HasMark(marks.Sensitive) || v.HasMark(marks.Ephemeral) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v, _ = v.Unmark()
|
v, _ = v.Unmark()
|
||||||
@@ -83,7 +82,6 @@ func ObjectValueID(obj cty.Value) (k, v string) {
|
|||||||
// name-extraction heuristics.
|
// name-extraction heuristics.
|
||||||
//
|
//
|
||||||
// This function will panic if the given value is not of an object type.
|
// This function will panic if the given value is not of an object type.
|
||||||
// TODO ephemeral - check how (and if) ephemeral marks should have their own logic here. Check unit tests too.
|
|
||||||
func ObjectValueName(obj cty.Value) (k, v string) {
|
func ObjectValueName(obj cty.Value) (k, v string) {
|
||||||
if obj.IsNull() || !obj.IsKnown() {
|
if obj.IsNull() || !obj.IsKnown() {
|
||||||
return "", ""
|
return "", ""
|
||||||
@@ -95,7 +93,7 @@ func ObjectValueName(obj cty.Value) (k, v string) {
|
|||||||
|
|
||||||
case atys["name"] == cty.String:
|
case atys["name"] == cty.String:
|
||||||
v := obj.GetAttr("name")
|
v := obj.GetAttr("name")
|
||||||
if v.HasMark(marks.Sensitive) {
|
if v.HasMark(marks.Sensitive) || v.HasMark(marks.Ephemeral) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v, _ = v.Unmark()
|
v, _ = v.Unmark()
|
||||||
@@ -106,7 +104,7 @@ func ObjectValueName(obj cty.Value) (k, v string) {
|
|||||||
|
|
||||||
case atys["tags"].IsMapType() && atys["tags"].ElementType() == cty.String:
|
case atys["tags"].IsMapType() && atys["tags"].ElementType() == cty.String:
|
||||||
tags := obj.GetAttr("tags")
|
tags := obj.GetAttr("tags")
|
||||||
if tags.IsNull() || !tags.IsWhollyKnown() || tags.HasMark(marks.Sensitive) {
|
if tags.IsNull() || !tags.IsWhollyKnown() || tags.HasMark(marks.Sensitive) || tags.HasMark(marks.Ephemeral) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
tags, _ = tags.Unmark()
|
tags, _ = tags.Unmark()
|
||||||
@@ -114,7 +112,7 @@ func ObjectValueName(obj cty.Value) (k, v string) {
|
|||||||
switch {
|
switch {
|
||||||
case tags.HasIndex(cty.StringVal("name")).RawEquals(cty.True):
|
case tags.HasIndex(cty.StringVal("name")).RawEquals(cty.True):
|
||||||
v := tags.Index(cty.StringVal("name"))
|
v := tags.Index(cty.StringVal("name"))
|
||||||
if v.HasMark(marks.Sensitive) {
|
if v.HasMark(marks.Sensitive) || v.HasMark(marks.Ephemeral) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v, _ = v.Unmark()
|
v, _ = v.Unmark()
|
||||||
@@ -125,7 +123,7 @@ func ObjectValueName(obj cty.Value) (k, v string) {
|
|||||||
case tags.HasIndex(cty.StringVal("Name")).RawEquals(cty.True):
|
case tags.HasIndex(cty.StringVal("Name")).RawEquals(cty.True):
|
||||||
// AWS-style naming convention
|
// AWS-style naming convention
|
||||||
v := tags.Index(cty.StringVal("Name"))
|
v := tags.Index(cty.StringVal("Name"))
|
||||||
if v.HasMark(marks.Sensitive) {
|
if v.HasMark(marks.Sensitive) || v.HasMark(marks.Ephemeral) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v, _ = v.Unmark()
|
v, _ = v.Unmark()
|
||||||
|
|||||||
@@ -46,6 +46,22 @@ func TestObjectValueIDOrName(t *testing.T) {
|
|||||||
[...]string{"", ""},
|
[...]string{"", ""},
|
||||||
[...]string{"id", "foo-123"},
|
[...]string{"id", "foo-123"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("foo-123").Mark(marks.Sensitive),
|
||||||
|
}),
|
||||||
|
[...]string{"", ""},
|
||||||
|
[...]string{"", ""},
|
||||||
|
[...]string{"", ""},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("foo-123").Mark(marks.Ephemeral),
|
||||||
|
}),
|
||||||
|
[...]string{"", ""},
|
||||||
|
[...]string{"", ""},
|
||||||
|
[...]string{"", ""},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
"id": cty.StringVal("foo-123"),
|
"id": cty.StringVal("foo-123"),
|
||||||
@@ -71,6 +87,14 @@ func TestObjectValueIDOrName(t *testing.T) {
|
|||||||
[...]string{"", ""},
|
[...]string{"", ""},
|
||||||
[...]string{"", ""},
|
[...]string{"", ""},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"name": cty.StringVal("awesome-bar").Mark(marks.Ephemeral),
|
||||||
|
}),
|
||||||
|
[...]string{"", ""},
|
||||||
|
[...]string{"", ""},
|
||||||
|
[...]string{"", ""},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
"name": cty.StringVal("awesome-foo"),
|
"name": cty.StringVal("awesome-foo"),
|
||||||
|
|||||||
@@ -369,8 +369,9 @@ func newDiagnosticDifference(diag tfdiags.Diagnostic) *jsonplan.Change {
|
|||||||
// we cannot generate the same diff when the values involved are marked as ephemeral.
|
// we cannot generate the same diff when the values involved are marked as ephemeral.
|
||||||
// Therefore, for situations like this, we will return no diagnostic diff, making
|
// Therefore, for situations like this, we will return no diagnostic diff, making
|
||||||
// the rendering of this to skip the diff part.
|
// the rendering of this to skip the diff part.
|
||||||
// TODO ephemeral - later we can find a better solution for this, like changing the type
|
// Later edit: As decided in opentofu/opentofu#3151, we decided
|
||||||
// of the Diagnostic.Difference so that it can hold a generic type that can do this.
|
// on not improving this diff rendering since it will show just a hint nonetheless.
|
||||||
|
// If later will be needed, we can reconsider.
|
||||||
if marks.Contains(lhs, marks.Ephemeral) || marks.Contains(rhs, marks.Ephemeral) {
|
if marks.Contains(lhs, marks.Ephemeral) || marks.Contains(rhs, marks.Ephemeral) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,13 @@ func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) co
|
|||||||
// values returned will be null.
|
// values returned will be null.
|
||||||
childChange := ComputeDiffForAttribute(childValue, attr, current)
|
childChange := ComputeDiffForAttribute(childValue, attr, current)
|
||||||
if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil {
|
||||||
// Don't record nil values at all in blocks.
|
// This validation is specifically added for `tofu show`.
|
||||||
continue
|
// Since "current" will be NoOp during rendering the output for `tofu show`,
|
||||||
|
// we need this validation to include the write-only attributes in the output.
|
||||||
|
if !attr.WriteOnly {
|
||||||
|
// Don't record nil values at all in blocks.
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes[key] = childChange
|
attributes[key] = childChange
|
||||||
|
|||||||
@@ -3451,6 +3451,34 @@ func TestWriteOnly_ComputeDiffForBlock(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, plans.Create, false),
|
}, plans.Create, false),
|
||||||
},
|
},
|
||||||
|
"write-only are shown during no-op too": {
|
||||||
|
block: jsonprovider.Block{
|
||||||
|
Attributes: map[string]*jsonprovider.Attribute{
|
||||||
|
"write_only_attr": {
|
||||||
|
WriteOnly: true,
|
||||||
|
AttributeType: unmarshalType(t, cty.String),
|
||||||
|
},
|
||||||
|
"regular_attr": {
|
||||||
|
AttributeType: unmarshalType(t, cty.String),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
change: structured.Change{
|
||||||
|
After: map[string]any{
|
||||||
|
"write_only_attr": nil,
|
||||||
|
"regular_attr": "test",
|
||||||
|
},
|
||||||
|
Before: map[string]any{
|
||||||
|
"write_only_attr": nil,
|
||||||
|
"regular_attr": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validator: renderers.ValidateBlock(
|
||||||
|
map[string]renderers.ValidateDiffFunction{
|
||||||
|
"write_only_attr": renderers.ValidateWriteOnly(plans.NoOp, false),
|
||||||
|
"regular_attr": renderers.ValidatePrimitive("test", "test", plans.NoOp, false),
|
||||||
|
}, nil, nil, nil, nil, plans.NoOp, false),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tt := range cases {
|
for name, tt := range cases {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...plans.Q
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if diff.change.Mode == jsonstate.EphemeralResourceMode {
|
if diff.change.Mode == jsonstate.EphemeralResourceMode {
|
||||||
// Do not render ephemeral changes. // TODO ephemeral add e2e test for this
|
// Do not render ephemeral changes.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ var (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO ephemeral - when implementing testing support for ephemeral resources, consider configuring ephemeral schema here
|
// TODO ephemeral testing support - when implementing testing support for ephemeral resources, consider configuring ephemeral schema here
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ func (h *countHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generati
|
|||||||
defer h.Unlock()
|
defer h.Unlock()
|
||||||
|
|
||||||
// We don't count anything for data resources and neither for the ephemeral ones.
|
// We don't count anything for data resources and neither for the ephemeral ones.
|
||||||
// TODO ephemeral - test this after the ephemeral resources are introduced entirely
|
|
||||||
if addr.Resource.Resource.Mode == addrs.DataResourceMode || addr.Resource.Resource.Mode == addrs.EphemeralResourceMode {
|
if addr.Resource.Resource.Mode == addrs.DataResourceMode || addr.Resource.Resource.Mode == addrs.EphemeralResourceMode {
|
||||||
return tofu.HookActionContinue, nil
|
return tofu.HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1096,12 +1096,12 @@ func (c *Config) transformOverriddenResourcesForTest(run *TestRun, file *TestFil
|
|||||||
}
|
}
|
||||||
|
|
||||||
if res.Mode != overrideRes.Mode {
|
if res.Mode != overrideRes.Mode {
|
||||||
// TODO ephemeral - include also the ephemeral resource and the test_file.go#override_ephemeral
|
// TODO ephemeral testing support - include also the ephemeral resource and the test_file.go#override_ephemeral
|
||||||
blockName, targetMode := blockNameOverrideResource, "data"
|
blockName, targetMode := blockNameOverrideResource, "data"
|
||||||
if overrideRes.Mode == addrs.DataResourceMode {
|
if overrideRes.Mode == addrs.DataResourceMode {
|
||||||
blockName, targetMode = blockNameOverrideData, "resource"
|
blockName, targetMode = blockNameOverrideData, "resource"
|
||||||
}
|
}
|
||||||
//It could be a warning, but for the sake of consistent UX let's make it an error
|
// It could be a warning, but for the sake of consistent UX let's make it an error
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: fmt.Sprintf("Unsupported `%v` target in `%v` block", targetMode, blockName),
|
Summary: fmt.Sprintf("Unsupported `%v` target in `%v` block", targetMode, blockName),
|
||||||
|
|||||||
@@ -263,7 +263,8 @@ type TestRunOptions struct {
|
|||||||
const (
|
const (
|
||||||
blockNameOverrideResource = "override_resource"
|
blockNameOverrideResource = "override_resource"
|
||||||
blockNameOverrideData = "override_data"
|
blockNameOverrideData = "override_data"
|
||||||
//blockNameOverrideEphemeral = "override_ephemeral" // TODO ephemeral uncomment this when testing support will be added for ephemerals
|
// TODO ephemeral testing support - uncomment this when testing support will be added for ephemerals
|
||||||
|
// blockNameOverrideEphemeral = "override_ephemeral"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OverrideResource contains information about a resource or data block to be overridden.
|
// OverrideResource contains information about a resource or data block to be overridden.
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder,
|
|||||||
if !hclsyntax.ValidIdentifier(name) {
|
if !hclsyntax.ValidIdentifier(name) {
|
||||||
name = string(hclwrite.TokensForValue(cty.StringVal(name)).Bytes())
|
name = string(hclwrite.TokensForValue(cty.StringVal(name)).Bytes())
|
||||||
}
|
}
|
||||||
fmt.Fprintf(buf, "%s = ", name)
|
_, _ = fmt.Fprintf(buf, "%s = ", name)
|
||||||
tok := hclwrite.TokensForValue(attrS.EmptyValue())
|
tok := hclwrite.TokensForValue(attrS.EmptyValue())
|
||||||
if _, err := tok.WriteTo(buf); err != nil {
|
if _, err := tok.WriteTo(buf); err != nil {
|
||||||
diags = diags.Append(&hcl.Diagnostic{
|
diags = diags.Append(&hcl.Diagnostic{
|
||||||
@@ -113,7 +113,7 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder,
|
|||||||
if !hclsyntax.ValidIdentifier(name) {
|
if !hclsyntax.ValidIdentifier(name) {
|
||||||
name = string(hclwrite.TokensForValue(cty.StringVal(name)).Bytes())
|
name = string(hclwrite.TokensForValue(cty.StringVal(name)).Bytes())
|
||||||
}
|
}
|
||||||
fmt.Fprintf(buf, "%s = ", name)
|
_, _ = fmt.Fprintf(buf, "%s = ", name)
|
||||||
tok := hclwrite.TokensForValue(attrS.EmptyValue())
|
tok := hclwrite.TokensForValue(attrS.EmptyValue())
|
||||||
if _, err := tok.WriteTo(buf); err != nil {
|
if _, err := tok.WriteTo(buf); err != nil {
|
||||||
diags = diags.Append(&hcl.Diagnostic{
|
diags = diags.Append(&hcl.Diagnostic{
|
||||||
@@ -130,7 +130,6 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder,
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO ephemeral - check how ephemeral should be integrated in this function. Check also the unit tests
|
|
||||||
func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, stateVal cty.Value, attrs map[string]*configschema.Attribute, indent int) tfdiags.Diagnostics {
|
func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, stateVal cty.Value, attrs map[string]*configschema.Attribute, indent int) tfdiags.Diagnostics {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
if len(attrs) == 0 {
|
if len(attrs) == 0 {
|
||||||
@@ -155,7 +154,7 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
|
|||||||
// Exclude computed-only attributes
|
// Exclude computed-only attributes
|
||||||
if attrS.Required || attrS.Optional {
|
if attrS.Required || attrS.Optional {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = ", name)
|
_, _ = fmt.Fprintf(buf, "%s = ", name)
|
||||||
|
|
||||||
var val cty.Value
|
var val cty.Value
|
||||||
if !stateVal.IsNull() && stateVal.Type().HasAttribute(name) {
|
if !stateVal.IsNull() && stateVal.Type().HasAttribute(name) {
|
||||||
@@ -164,10 +163,10 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
|
|||||||
val = attrS.EmptyValue()
|
val = attrS.EmptyValue()
|
||||||
}
|
}
|
||||||
if attrS.Sensitive || val.HasMark(marks.Sensitive) {
|
if attrS.Sensitive || val.HasMark(marks.Sensitive) {
|
||||||
buf.WriteString("null # sensitive")
|
_, _ = fmt.Fprintf(buf, "null # sensitive%s", writeOnlyComment(attrS, false))
|
||||||
} else {
|
} else {
|
||||||
if val.Type() == cty.String {
|
if val.Type() == cty.String {
|
||||||
unmarked, marks := val.Unmark()
|
unmarked, valMarks := val.Unmark()
|
||||||
|
|
||||||
// SHAMELESS HACK: If we have "" for an optional value, assume
|
// SHAMELESS HACK: If we have "" for an optional value, assume
|
||||||
// it is actually null, due to the legacy SDK.
|
// it is actually null, due to the legacy SDK.
|
||||||
@@ -176,8 +175,8 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// re-mark the value if it was marked originally
|
// re-mark the value if it was marked originally
|
||||||
if len(marks) > 0 {
|
if len(valMarks) > 0 {
|
||||||
val = unmarked.Mark(marks)
|
val = unmarked.Mark(valMarks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +191,7 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
|
|||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
_, _ = fmt.Fprintf(buf, "%s", writeOnlyComment(attrS, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
@@ -228,7 +228,7 @@ func writeConfigNestedBlock(addr addrs.AbsResourceInstance, buf *strings.Builder
|
|||||||
switch schema.Nesting {
|
switch schema.Nesting {
|
||||||
case configschema.NestingSingle, configschema.NestingGroup:
|
case configschema.NestingSingle, configschema.NestingGroup:
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s {", name)
|
_, _ = fmt.Fprintf(buf, "%s {", name)
|
||||||
writeBlockTypeConstraint(buf, schema)
|
writeBlockTypeConstraint(buf, schema)
|
||||||
diags = diags.Append(writeConfigAttributes(addr, buf, schema.Attributes, indent+2))
|
diags = diags.Append(writeConfigAttributes(addr, buf, schema.Attributes, indent+2))
|
||||||
diags = diags.Append(writeConfigBlocks(addr, buf, schema.BlockTypes, indent+2))
|
diags = diags.Append(writeConfigBlocks(addr, buf, schema.BlockTypes, indent+2))
|
||||||
@@ -236,7 +236,7 @@ func writeConfigNestedBlock(addr addrs.AbsResourceInstance, buf *strings.Builder
|
|||||||
return diags
|
return diags
|
||||||
case configschema.NestingList, configschema.NestingSet:
|
case configschema.NestingList, configschema.NestingSet:
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s {", name)
|
_, _ = fmt.Fprintf(buf, "%s {", name)
|
||||||
writeBlockTypeConstraint(buf, schema)
|
writeBlockTypeConstraint(buf, schema)
|
||||||
diags = diags.Append(writeConfigAttributes(addr, buf, schema.Attributes, indent+2))
|
diags = diags.Append(writeConfigAttributes(addr, buf, schema.Attributes, indent+2))
|
||||||
diags = diags.Append(writeConfigBlocks(addr, buf, schema.BlockTypes, indent+2))
|
diags = diags.Append(writeConfigBlocks(addr, buf, schema.BlockTypes, indent+2))
|
||||||
@@ -245,7 +245,7 @@ func writeConfigNestedBlock(addr addrs.AbsResourceInstance, buf *strings.Builder
|
|||||||
case configschema.NestingMap:
|
case configschema.NestingMap:
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
// we use an arbitrary placeholder key (block label) "key"
|
// we use an arbitrary placeholder key (block label) "key"
|
||||||
fmt.Fprintf(buf, "%s \"key\" {", name)
|
_, _ = fmt.Fprintf(buf, "%s \"key\" {", name)
|
||||||
writeBlockTypeConstraint(buf, schema)
|
writeBlockTypeConstraint(buf, schema)
|
||||||
diags = diags.Append(writeConfigAttributes(addr, buf, schema.Attributes, indent+2))
|
diags = diags.Append(writeConfigAttributes(addr, buf, schema.Attributes, indent+2))
|
||||||
diags = diags.Append(writeConfigBlocks(addr, buf, schema.BlockTypes, indent+2))
|
diags = diags.Append(writeConfigBlocks(addr, buf, schema.BlockTypes, indent+2))
|
||||||
@@ -262,7 +262,7 @@ func writeConfigNestedTypeAttribute(addr addrs.AbsResourceInstance, buf *strings
|
|||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = ", name)
|
_, _ = fmt.Fprintf(buf, "%s = ", name)
|
||||||
|
|
||||||
switch schema.NestedType.Nesting {
|
switch schema.NestedType.Nesting {
|
||||||
case configschema.NestingSingle:
|
case configschema.NestingSingle:
|
||||||
@@ -326,7 +326,6 @@ func writeConfigBlocksFromExisting(addr addrs.AbsResourceInstance, buf *strings.
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO ephemeral - check how ephemeral should be integrated in this function. Check also the unit tests
|
|
||||||
func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, name string, schema *configschema.Attribute, stateVal cty.Value, indent int) tfdiags.Diagnostics {
|
func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, name string, schema *configschema.Attribute, stateVal cty.Value, indent int) tfdiags.Diagnostics {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
@@ -334,7 +333,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
case configschema.NestingSingle:
|
case configschema.NestingSingle:
|
||||||
if schema.Sensitive || stateVal.HasMark(marks.Sensitive) {
|
if schema.Sensitive || stateVal.HasMark(marks.Sensitive) {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = {} # sensitive\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = {} # sensitive%s\n", name, writeOnlyComment(schema, false))
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,12 +349,12 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
// There is a difference between a null object, and an object with
|
// There is a difference between a null object, and an object with
|
||||||
// no attributes.
|
// no attributes.
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = null\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = null%s\n", name, writeOnlyComment(schema, true))
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = {\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = {\n", name)
|
||||||
diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, nestedVal, schema.NestedType.Attributes, indent+2))
|
diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, nestedVal, schema.NestedType.Attributes, indent+2))
|
||||||
buf.WriteString("}\n")
|
buf.WriteString("}\n")
|
||||||
return diags
|
return diags
|
||||||
@@ -364,7 +363,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
|
|
||||||
if schema.Sensitive || stateVal.HasMark(marks.Sensitive) {
|
if schema.Sensitive || stateVal.HasMark(marks.Sensitive) {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = [] # sensitive\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = [] # sensitive%s\n", name, writeOnlyComment(schema, false))
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,15 +371,14 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
if listVals == nil {
|
if listVals == nil {
|
||||||
// There is a difference between an empty list and a null list
|
// There is a difference between an empty list and a null list
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = null\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = null%s\n", name, writeOnlyComment(schema, true))
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = [\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = [\n", name)
|
||||||
for i := range listVals {
|
for i := range listVals {
|
||||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
|
|
||||||
// The entire element is marked.
|
// The entire element is marked.
|
||||||
if listVals[i].HasMark(marks.Sensitive) {
|
if listVals[i].HasMark(marks.Sensitive) {
|
||||||
buf.WriteString("{}, # sensitive\n")
|
buf.WriteString("{}, # sensitive\n")
|
||||||
@@ -399,7 +397,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
case configschema.NestingMap:
|
case configschema.NestingMap:
|
||||||
if schema.Sensitive || stateVal.HasMark(marks.Sensitive) {
|
if schema.Sensitive || stateVal.HasMark(marks.Sensitive) {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = {} # sensitive\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = {} # sensitive%s\n", name, writeOnlyComment(schema, false))
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +405,7 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
if attr.IsNull() {
|
if attr.IsNull() {
|
||||||
// There is a difference between an empty map and a null map.
|
// There is a difference between an empty map and a null map.
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = null\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = null%s\n", name, writeOnlyComment(schema, true))
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,10 +418,10 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s = {\n", name)
|
_, _ = fmt.Fprintf(buf, "%s = {\n", name)
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||||
fmt.Fprintf(buf, "%s = {", key)
|
_, _ = fmt.Fprintf(buf, "%s = {", key)
|
||||||
|
|
||||||
// This entire value is marked
|
// This entire value is marked
|
||||||
if vals[key].HasMark(marks.Sensitive) {
|
if vals[key].HasMark(marks.Sensitive) {
|
||||||
@@ -446,7 +444,6 @@ func writeConfigNestedTypeAttributeFromExisting(addr addrs.AbsResourceInstance,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO ephemeral - check how ephemeral should be integrated in this function. Check also the unit tests
|
|
||||||
func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, name string, schema *configschema.NestedBlock, stateVal cty.Value, indent int) tfdiags.Diagnostics {
|
func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *strings.Builder, name string, schema *configschema.NestedBlock, stateVal cty.Value, indent int) tfdiags.Diagnostics {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
@@ -456,7 +453,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s {", name)
|
_, _ = fmt.Fprintf(buf, "%s {", name)
|
||||||
|
|
||||||
// If the entire value is marked, don't print any nested attributes
|
// If the entire value is marked, don't print any nested attributes
|
||||||
if stateVal.HasMark(marks.Sensitive) {
|
if stateVal.HasMark(marks.Sensitive) {
|
||||||
@@ -471,13 +468,13 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str
|
|||||||
case configschema.NestingList, configschema.NestingSet:
|
case configschema.NestingList, configschema.NestingSet:
|
||||||
if stateVal.HasMark(marks.Sensitive) {
|
if stateVal.HasMark(marks.Sensitive) {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s {} # sensitive\n", name)
|
_, _ = fmt.Fprintf(buf, "%s {} # sensitive\n", name)
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
listVals := ctyCollectionValues(stateVal)
|
listVals := ctyCollectionValues(stateVal)
|
||||||
for i := range listVals {
|
for i := range listVals {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s {\n", name)
|
_, _ = fmt.Fprintf(buf, "%s {\n", name)
|
||||||
diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, listVals[i], schema.Attributes, indent+2))
|
diags = diags.Append(writeConfigAttributesFromExisting(addr, buf, listVals[i], schema.Attributes, indent+2))
|
||||||
diags = diags.Append(writeConfigBlocksFromExisting(addr, buf, listVals[i], schema.BlockTypes, indent+2))
|
diags = diags.Append(writeConfigBlocksFromExisting(addr, buf, listVals[i], schema.BlockTypes, indent+2))
|
||||||
buf.WriteString("}\n")
|
buf.WriteString("}\n")
|
||||||
@@ -486,7 +483,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str
|
|||||||
case configschema.NestingMap:
|
case configschema.NestingMap:
|
||||||
// If the entire value is marked, don't print any nested attributes
|
// If the entire value is marked, don't print any nested attributes
|
||||||
if stateVal.HasMark(marks.Sensitive) {
|
if stateVal.HasMark(marks.Sensitive) {
|
||||||
fmt.Fprintf(buf, "%s {} # sensitive\n", name)
|
_, _ = fmt.Fprintf(buf, "%s {} # sensitive\n", name)
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +496,7 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str
|
|||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
buf.WriteString(strings.Repeat(" ", indent))
|
buf.WriteString(strings.Repeat(" ", indent))
|
||||||
fmt.Fprintf(buf, "%s %q {", name, key)
|
_, _ = fmt.Fprintf(buf, "%s %q {", name, key)
|
||||||
// This entire map element is marked
|
// This entire map element is marked
|
||||||
if vals[key].HasMark(marks.Sensitive) {
|
if vals[key].HasMark(marks.Sensitive) {
|
||||||
buf.WriteString("} # sensitive\n")
|
buf.WriteString("} # sensitive\n")
|
||||||
@@ -526,9 +523,9 @@ func writeAttrTypeConstraint(buf *strings.Builder, schema *configschema.Attribut
|
|||||||
}
|
}
|
||||||
|
|
||||||
if schema.NestedType != nil {
|
if schema.NestedType != nil {
|
||||||
fmt.Fprintf(buf, "%s\n", schema.NestedType.ImpliedType().FriendlyName())
|
_, _ = fmt.Fprintf(buf, "%s\n", schema.NestedType.ImpliedType().FriendlyName())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(buf, "%s\n", schema.Type.FriendlyName())
|
_, _ = fmt.Fprintf(buf, "%s\n", schema.Type.FriendlyName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,15 +543,15 @@ func ctyCollectionValues(val cty.Value) []cty.Value {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var len int
|
var length int
|
||||||
if val.IsMarked() {
|
if val.IsMarked() {
|
||||||
val, _ = val.Unmark()
|
val, _ = val.Unmark()
|
||||||
len = val.LengthInt()
|
length = val.LengthInt()
|
||||||
} else {
|
} else {
|
||||||
len = val.LengthInt()
|
length = val.LengthInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := make([]cty.Value, 0, len)
|
ret := make([]cty.Value, 0, length)
|
||||||
for it := val.ElementIterator(); it.Next(); {
|
for it := val.ElementIterator(); it.Next(); {
|
||||||
_, value := it.Element()
|
_, value := it.Element()
|
||||||
ret = append(ret, value)
|
ret = append(ret, value)
|
||||||
@@ -580,7 +577,7 @@ func omitUnknowns(val cty.Value) cty.Value {
|
|||||||
case ty.IsPrimitiveType():
|
case ty.IsPrimitiveType():
|
||||||
return val
|
return val
|
||||||
case ty.IsListType() || ty.IsTupleType() || ty.IsSetType():
|
case ty.IsListType() || ty.IsTupleType() || ty.IsSetType():
|
||||||
unmarked, marks := val.Unmark()
|
unmarked, valMarks := val.Unmark()
|
||||||
var vals []cty.Value
|
var vals []cty.Value
|
||||||
it := unmarked.ElementIterator()
|
it := unmarked.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
@@ -598,9 +595,9 @@ func omitUnknowns(val cty.Value) cty.Value {
|
|||||||
// may have caused the individual elements to have different types,
|
// may have caused the individual elements to have different types,
|
||||||
// and we're doing this work to produce JSON anyway and JSON marshalling
|
// and we're doing this work to produce JSON anyway and JSON marshalling
|
||||||
// represents all of these sequence types as an array.
|
// represents all of these sequence types as an array.
|
||||||
return cty.TupleVal(vals).WithMarks(marks)
|
return cty.TupleVal(vals).WithMarks(valMarks)
|
||||||
case ty.IsMapType() || ty.IsObjectType():
|
case ty.IsMapType() || ty.IsObjectType():
|
||||||
unmarked, marks := val.Unmark()
|
unmarked, valMarks := val.Unmark()
|
||||||
vals := make(map[string]cty.Value)
|
vals := make(map[string]cty.Value)
|
||||||
it := unmarked.ElementIterator()
|
it := unmarked.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
@@ -614,7 +611,7 @@ func omitUnknowns(val cty.Value) cty.Value {
|
|||||||
// may have caused the individual elements to have different types,
|
// may have caused the individual elements to have different types,
|
||||||
// and we're doing this work to produce JSON anyway and JSON marshalling
|
// and we're doing this work to produce JSON anyway and JSON marshalling
|
||||||
// represents both of these mapping types as an object.
|
// represents both of these mapping types as an object.
|
||||||
return cty.ObjectVal(vals).WithMarks(marks)
|
return cty.ObjectVal(vals).WithMarks(valMarks)
|
||||||
default:
|
default:
|
||||||
// Should never happen, since the above should cover all types
|
// Should never happen, since the above should cover all types
|
||||||
panic(fmt.Sprintf("omitUnknowns cannot handle %#v", val))
|
panic(fmt.Sprintf("omitUnknowns cannot handle %#v", val))
|
||||||
@@ -661,3 +658,13 @@ func wrapAsJSONEncodeFunctionCall(v cty.Value) (hclwrite.Tokens, error) {
|
|||||||
|
|
||||||
return tokens, nil
|
return tokens, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeOnlyComment(attr *configschema.Attribute, startComment bool) string {
|
||||||
|
if !attr.WriteOnly {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if startComment {
|
||||||
|
return " # write-only"
|
||||||
|
}
|
||||||
|
return " write-only"
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ func TestConfigGeneration(t *testing.T) {
|
|||||||
Computed: computed,
|
Computed: computed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
writeOnlyAttr := func(attribute *configschema.Attribute) *configschema.Attribute {
|
||||||
|
attribute.WriteOnly = true
|
||||||
|
return attribute
|
||||||
|
}
|
||||||
|
|
||||||
tcs := map[string]struct {
|
tcs := map[string]struct {
|
||||||
schema *configschema.Block
|
schema *configschema.Block
|
||||||
@@ -724,6 +728,59 @@ resource "tfcoremock_simple_resource" "example" {
|
|||||||
sensitive_number = null # sensitive
|
sensitive_number = null # sensitive
|
||||||
sensitive_object = null # sensitive
|
sensitive_object = null # sensitive
|
||||||
sensitive_string = null # sensitive
|
sensitive_string = null # sensitive
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
"simple_resource_with_write_only_and_sensitive": {
|
||||||
|
// Write-only adds a new comment so we want to check that too
|
||||||
|
|
||||||
|
schema: &configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"sensitive_string": writeOnlyAttr(sensitiveVal(cty.String, false, true, false)),
|
||||||
|
"sensitive_list": writeOnlyAttr(sensitiveVal(cty.List(cty.String), false, true, false)),
|
||||||
|
"sensitive_map": writeOnlyAttr(sensitiveVal(cty.Map(cty.String), false, true, false)),
|
||||||
|
"sensitive_object": writeOnlyAttr(sensitiveVal(cty.Object(map[string]cty.Type{}), false, true, false)),
|
||||||
|
"single_nested_attribute": {
|
||||||
|
NestedType: &configschema.Object{
|
||||||
|
Nesting: configschema.NestingSingle,
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"single_nested_attribute_string": writeOnlyAttr(sensitiveVal(cty.String, false, true, false)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
addr: addrs.AbsResourceInstance{
|
||||||
|
Module: nil,
|
||||||
|
Resource: addrs.ResourceInstance{
|
||||||
|
Resource: addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "tfcoremock_simple_resource",
|
||||||
|
Name: "example",
|
||||||
|
},
|
||||||
|
Key: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provider: addrs.LocalProviderConfig{
|
||||||
|
LocalName: "tfcoremock",
|
||||||
|
},
|
||||||
|
value: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"sensitive_string": cty.StringVal("sensitive").Mark(marks.Sensitive),
|
||||||
|
"sensitive_list": cty.ListVal([]cty.Value{cty.StringVal("sensitive")}).Mark(marks.Sensitive),
|
||||||
|
"sensitive_map": cty.MapVal(map[string]cty.Value{"key": cty.StringVal("sensitive")}).Mark(marks.Sensitive),
|
||||||
|
"sensitive_object": cty.ObjectVal(map[string]cty.Value{}).Mark(marks.Sensitive),
|
||||||
|
"single_nested_attribute": cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"single_nested_attribute_string": cty.StringVal("random"),
|
||||||
|
})}),
|
||||||
|
expected: `
|
||||||
|
resource "tfcoremock_simple_resource" "example" {
|
||||||
|
sensitive_list = null # sensitive write-only
|
||||||
|
sensitive_map = null # sensitive write-only
|
||||||
|
sensitive_object = null # sensitive write-only
|
||||||
|
sensitive_string = null # sensitive write-only
|
||||||
|
single_nested_attribute = {
|
||||||
|
single_nested_attribute_string = null # sensitive write-only
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
"simple_resource_with_all_sensitive_computed_values": {
|
"simple_resource_with_all_sensitive_computed_values": {
|
||||||
|
|||||||
@@ -135,9 +135,6 @@ func performTypeAndValueChecks(expr hcl.Expression, hclCtx *hcl.EvalContext, all
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO ephemeral - check how ephemeral should impact this function. Check also the unit tests
|
|
||||||
//
|
|
||||||
// Christian: it should follow the same conventions as Sensitive and only be allowed in each.value and not in each.key.
|
|
||||||
func performValueChecks(expr hcl.Expression, hclCtx *hcl.EvalContext, allowUnknown bool, forEachVal cty.Value, typeCheckVal cty.Value, errInvalidUnknownDetail string, excludableAddr addrs.Targetable) (cty.Value, tfdiags.Diagnostics) {
|
func performValueChecks(expr hcl.Expression, hclCtx *hcl.EvalContext, allowUnknown bool, forEachVal cty.Value, typeCheckVal cty.Value, errInvalidUnknownDetail string, excludableAddr addrs.Targetable) (cty.Value, tfdiags.Diagnostics) {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
ty := forEachVal.Type()
|
ty := forEachVal.Type()
|
||||||
@@ -186,6 +183,18 @@ func performValueChecks(expr hcl.Expression, hclCtx *hcl.EvalContext, allowUnkno
|
|||||||
})
|
})
|
||||||
resultVal = cty.NullVal(ty)
|
resultVal = cty.NullVal(ty)
|
||||||
}
|
}
|
||||||
|
if forEachVal.HasMark(marks.Ephemeral) {
|
||||||
|
diags = diags.Append(&hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
||||||
|
Subject: expr.Range().Ptr(),
|
||||||
|
Expression: expr,
|
||||||
|
EvalContext: hclCtx,
|
||||||
|
Extra: DiagnosticCausedByConfidentialValues(true),
|
||||||
|
})
|
||||||
|
resultVal = cty.NullVal(ty)
|
||||||
|
}
|
||||||
|
|
||||||
return resultVal, diags
|
return resultVal, diags
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,25 @@ func TestEvaluateForEachExpression_multi_errors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ephemeral marked list": {
|
||||||
|
hcltest.MockExprLiteral(cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Ephemeral)),
|
||||||
|
[]struct {
|
||||||
|
Summary string
|
||||||
|
DetailSubstring string
|
||||||
|
CausedBySensitive bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Invalid for_each argument",
|
||||||
|
"Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Invalid for_each argument",
|
||||||
|
"must be a map, or set of strings, and you have provided a value of type list",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"marked tuple": {
|
"marked tuple": {
|
||||||
hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Sensitive)),
|
hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Sensitive)),
|
||||||
[]struct {
|
[]struct {
|
||||||
@@ -75,6 +94,25 @@ func TestEvaluateForEachExpression_multi_errors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ephemeral marked tuple": {
|
||||||
|
hcltest.MockExprLiteral(cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("a")}).Mark(marks.Ephemeral)),
|
||||||
|
[]struct {
|
||||||
|
Summary string
|
||||||
|
DetailSubstring string
|
||||||
|
CausedBySensitive bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Invalid for_each argument",
|
||||||
|
"Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Invalid for_each argument",
|
||||||
|
"must be a map, or set of strings, and you have provided a value of type tuple",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"marked string": {
|
"marked string": {
|
||||||
hcltest.MockExprLiteral(cty.StringVal("a").Mark(marks.Sensitive)),
|
hcltest.MockExprLiteral(cty.StringVal("a").Mark(marks.Sensitive)),
|
||||||
[]struct {
|
[]struct {
|
||||||
@@ -94,6 +132,25 @@ func TestEvaluateForEachExpression_multi_errors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ephemeral marked string": {
|
||||||
|
hcltest.MockExprLiteral(cty.StringVal("a").Mark(marks.Ephemeral)),
|
||||||
|
[]struct {
|
||||||
|
Summary string
|
||||||
|
DetailSubstring string
|
||||||
|
CausedBySensitive bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Invalid for_each argument",
|
||||||
|
"Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments. If used, the ephemeral value could be exposed as a resource instance key.",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Invalid for_each argument",
|
||||||
|
"must be a map, or set of strings, and you have provided a value of type string",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
@@ -165,6 +222,17 @@ func TestEvaluateForEachExpressionValueTuple(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ephemeral tuple": {
|
||||||
|
Value: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}).Mark(marks.Ephemeral),
|
||||||
|
expectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"unknown tuple": {
|
"unknown tuple": {
|
||||||
Value: cty.UnknownVal(cty.Tuple([]cty.Type{cty.String})),
|
Value: cty.UnknownVal(cty.Tuple([]cty.Type{cty.String})),
|
||||||
expectedErrs: []expectedErr{
|
expectedErrs: []expectedErr{
|
||||||
@@ -782,6 +850,77 @@ func TestEvaluateForEach(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
PlanReturnValue: map[string]cty.Value{},
|
PlanReturnValue: map[string]cty.Value{},
|
||||||
},
|
},
|
||||||
|
"ephemeral_tuple": {
|
||||||
|
Input: cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}).Mark(marks.Ephemeral),
|
||||||
|
ValidateExpectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ValidateReturnValue: cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})),
|
||||||
|
PlanExpectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "argument must be a map, or set of strings, and you have provided a value of type tuple.",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PlanReturnValue: map[string]cty.Value{},
|
||||||
|
},
|
||||||
|
"ephemeral_set": {
|
||||||
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a")}).Mark(marks.Ephemeral),
|
||||||
|
ValidateExpectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
}},
|
||||||
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
||||||
|
PlanExpectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
}},
|
||||||
|
PlanReturnValue: map[string]cty.Value{},
|
||||||
|
},
|
||||||
|
"ephemeral_set_elements": {
|
||||||
|
Input: cty.SetVal([]cty.Value{cty.StringVal("a").Mark(marks.Ephemeral)}),
|
||||||
|
ValidateExpectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
}},
|
||||||
|
ValidateReturnValue: cty.NullVal(cty.Set(cty.String)),
|
||||||
|
PlanExpectedErrs: []expectedErr{
|
||||||
|
{
|
||||||
|
Summary: "Invalid for_each argument",
|
||||||
|
Detail: "Ephemeral values, or values derived from ephemeral values, cannot be used as for_each arguments",
|
||||||
|
CausedByUnknown: false,
|
||||||
|
CausedBySensitive: true,
|
||||||
|
}},
|
||||||
|
PlanReturnValue: map[string]cty.Value{},
|
||||||
|
},
|
||||||
"string": {
|
"string": {
|
||||||
Input: cty.StringVal("i am definitely a set"),
|
Input: cty.StringVal("i am definitely a set"),
|
||||||
ValidateExpectedErrs: []expectedErr{
|
ValidateExpectedErrs: []expectedErr{
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO ephemeral - should this function be aware of the ephemeral mark? This needs proper testing and investigation
|
|
||||||
func assertAttributeCompatible(plannedV, actualV cty.Value, attrS *configschema.Attribute, path cty.Path) []error {
|
func assertAttributeCompatible(plannedV, actualV cty.Value, attrS *configschema.Attribute, path cty.Path) []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func ProposedNew(schema *configschema.Block, prior, config cty.Value) cty.Value
|
|||||||
return proposedNew(schema, prior, config)
|
return proposedNew(schema, prior, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlannedDataResourceObject is similar to proposedNewBlock but tailored for
|
// PlannedUnknownObject is similar to proposedNewBlock but tailored for
|
||||||
// planning data resources in particular. Specifically, it replaces the values
|
// planning data resources in particular. Specifically, it replaces the values
|
||||||
// of any Computed attributes not set in the configuration with an unknown
|
// of any Computed attributes not set in the configuration with an unknown
|
||||||
// value, which serves as a placeholder for a value to be filled in by the
|
// value, which serves as a placeholder for a value to be filled in by the
|
||||||
@@ -63,7 +63,16 @@ func ProposedNew(schema *configschema.Block, prior, config cty.Value) cty.Value
|
|||||||
// passing the proposedNewBlock result into a provider's PlanResourceChange
|
// passing the proposedNewBlock result into a provider's PlanResourceChange
|
||||||
// function, assuming a fixed implementation of PlanResourceChange that just
|
// function, assuming a fixed implementation of PlanResourceChange that just
|
||||||
// fills in unknown values as needed.
|
// fills in unknown values as needed.
|
||||||
func PlannedDataResourceObject(schema *configschema.Block, config cty.Value) cty.Value {
|
//
|
||||||
|
// This is also used for generating the planned value of an ephemeral resource when
|
||||||
|
// that is deferred. By design, ephemeral resources opening requires the configuration
|
||||||
|
// of the block to be fully known. Therefore, when there is at least one unknown
|
||||||
|
// attribute, we defer the execution until we resolve all the unknowns, and only
|
||||||
|
// then we open the ephemeral. Until then, this particular function helps with
|
||||||
|
// generating a placeholder for the ephemeral resource based on its schema.
|
||||||
|
// Ephemeral resources are not stored into the state, so every newly planned value
|
||||||
|
// is based only on the configuration and its schema.
|
||||||
|
func PlannedUnknownObject(schema *configschema.Block, config cty.Value) cty.Value {
|
||||||
// Our trick here is to run the proposedNewBlock logic with an
|
// Our trick here is to run the proposedNewBlock logic with an
|
||||||
// entirely-unknown prior value. Because of cty's unknown short-circuit
|
// entirely-unknown prior value. Because of cty's unknown short-circuit
|
||||||
// behavior, any operation on prior returns another unknown, and so
|
// behavior, any operation on prior returns another unknown, and so
|
||||||
@@ -73,16 +82,6 @@ func PlannedDataResourceObject(schema *configschema.Block, config cty.Value) cty
|
|||||||
return proposedNew(schema, prior, config)
|
return proposedNew(schema, prior, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlannedEphemeralResourceObject is exactly as PlannedDataResourceObject, but we
|
|
||||||
// want to have a different copy of it to emphasize the special handling of
|
|
||||||
// this type of resource.
|
|
||||||
// Ephemeral resources are not stored into the state, so every newly planned value
|
|
||||||
// is based only on the configuration and its schema.
|
|
||||||
func PlannedEphemeralResourceObject(schema *configschema.Block, config cty.Value) cty.Value {
|
|
||||||
prior := cty.UnknownVal(schema.ImpliedType())
|
|
||||||
return proposedNew(schema, prior, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func proposedNew(schema *configschema.Block, prior, config cty.Value) cty.Value {
|
func proposedNew(schema *configschema.Block, prior, config cty.Value) cty.Value {
|
||||||
if config.IsNull() || !config.IsKnown() {
|
if config.IsNull() || !config.IsKnown() {
|
||||||
// A block config should never be null at this point. The only nullable
|
// A block config should never be null at this point. The only nullable
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ type GRPCProvider struct {
|
|||||||
|
|
||||||
var _ providers.Interface = new(GRPCProvider)
|
var _ providers.Interface = new(GRPCProvider)
|
||||||
|
|
||||||
// TODO ephemeral - double check all of the usages of this to be sure that the block.ephemeral for ephemeral resources is used accordingly.
|
|
||||||
func (p *GRPCProvider) GetProviderSchema(ctx context.Context) (resp providers.GetProviderSchemaResponse) {
|
func (p *GRPCProvider) GetProviderSchema(ctx context.Context) (resp providers.GetProviderSchemaResponse) {
|
||||||
logger.Trace("GRPCProvider.v6: GetProviderSchema")
|
logger.Trace("GRPCProvider.v6: GetProviderSchema")
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ func (s simple) Close(_ context.Context) error {
|
|||||||
|
|
||||||
func waitIfRequested(m map[string]cty.Value) {
|
func waitIfRequested(m map[string]cty.Value) {
|
||||||
// This is a special case that can be used together with ephemeral resources to be able to test the renewal process.
|
// This is a special case that can be used together with ephemeral resources to be able to test the renewal process.
|
||||||
// When the "value" attribute of the resource is containing "with-renew" it will return later to allow
|
// When the "value_wo" attribute of the resource is containing "with-renew" it will return later to allow
|
||||||
// the ephemeral resource to call renew at least once. Check also OpenEphemeralResource.
|
// the ephemeral resource to call renew at least once. Check also OpenEphemeralResource.
|
||||||
if v, ok := m["value_wo"]; ok && !v.IsNull() && strings.Contains(v.AsString(), "with-renew") {
|
if v, ok := m["value_wo"]; ok && !v.IsNull() && strings.Contains(v.AsString(), "with-renew") {
|
||||||
<-time.After(time.Second)
|
<-time.After(time.Second)
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ func (s simple) Close(_ context.Context) error {
|
|||||||
|
|
||||||
func waitIfRequested(m map[string]cty.Value) {
|
func waitIfRequested(m map[string]cty.Value) {
|
||||||
// This is a special case that can be used together with ephemeral resources to be able to test the renewal process.
|
// This is a special case that can be used together with ephemeral resources to be able to test the renewal process.
|
||||||
// When the "value" attribute of the resource is containing "with-renew" it will return later to allow
|
// When the "value_wo" attribute of the resource is containing "with-renew" it will return later to allow
|
||||||
// the ephemeral resource to call renew at least once. Check also OpenEphemeralResource.
|
// the ephemeral resource to call renew at least once. Check also OpenEphemeralResource.
|
||||||
if v, ok := m["value_wo"]; ok && !v.IsNull() && strings.Contains(v.AsString(), "with-renew") {
|
if v, ok := m["value_wo"]; ok && !v.IsNull() && strings.Contains(v.AsString(), "with-renew") {
|
||||||
<-time.After(time.Second)
|
<-time.After(time.Second)
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import (
|
|||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO ephemeral - check how ephemeral marks can be integrated in this function. Don't forget about unit tests
|
|
||||||
|
|
||||||
// FormatValue formats a value in a way that resembles OpenTofu language syntax
|
// FormatValue formats a value in a way that resembles OpenTofu language syntax
|
||||||
// and uses the type conversion functions where necessary to indicate exactly
|
// and uses the type conversion functions where necessary to indicate exactly
|
||||||
// what type it is given, so that equality test failures can be quickly
|
// what type it is given, so that equality test failures can be quickly
|
||||||
@@ -27,6 +25,9 @@ func FormatValue(v cty.Value, indent int) string {
|
|||||||
if v.HasMark(marks.Sensitive) {
|
if v.HasMark(marks.Sensitive) {
|
||||||
return "(sensitive value)"
|
return "(sensitive value)"
|
||||||
}
|
}
|
||||||
|
if v.HasMark(marks.Ephemeral) {
|
||||||
|
return "(ephemeral value)"
|
||||||
|
}
|
||||||
if v.IsNull() {
|
if v.IsNull() {
|
||||||
return formatNullValue(v.Type())
|
return formatNullValue(v.Type())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,10 @@ EOT_`,
|
|||||||
cty.StringVal("a sensitive value").Mark(marks.Sensitive),
|
cty.StringVal("a sensitive value").Mark(marks.Sensitive),
|
||||||
"(sensitive value)",
|
"(sensitive value)",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("an ephemeral value").Mark(marks.Ephemeral),
|
||||||
|
"(ephemeral value)",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|||||||
@@ -1069,7 +1069,7 @@ func (d *evaluationStateData) GetOutput(_ context.Context, addr addrs.OutputValu
|
|||||||
if output.Sensitive {
|
if output.Sensitive {
|
||||||
val = val.Mark(marks.Sensitive)
|
val = val.Mark(marks.Sensitive)
|
||||||
}
|
}
|
||||||
// TODO ephemeral - this GetOutput is used only during `tofu test` against root module outputs.
|
// TODO ephemeral testing support - this GetOutput is used only during `tofu test` against root module outputs.
|
||||||
// Therefore, since only the root module outputs can get in here, there is no reason to mark
|
// Therefore, since only the root module outputs can get in here, there is no reason to mark
|
||||||
// values with ephemeral. Reanalyse this when implementing the testing support.
|
// values with ephemeral. Reanalyse this when implementing the testing support.
|
||||||
// if config.Ephemeral {
|
// if config.Ephemeral {
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ func TestEvaluatorGetOutputValue(t *testing.T) {
|
|||||||
t.Errorf("wrong result %#v; want %#v", got, want)
|
t.Errorf("wrong result %#v; want %#v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO ephemeral - uncomment the line with the ephemeral mark once the testing support implementation is done
|
// TODO ephemeral testing support - uncomment the line with the ephemeral mark once the testing support implementation is done
|
||||||
// want = cty.StringVal("third").Mark(marks.Ephemeral)
|
// want = cty.StringVal("third").Mark(marks.Ephemeral)
|
||||||
want = cty.StringVal("third")
|
want = cty.StringVal("third")
|
||||||
got, diags = scope.Data.GetOutput(t.Context(), addrs.OutputValue{
|
got, diags = scope.Data.GetOutput(t.Context(), addrs.OutputValue{
|
||||||
|
|||||||
@@ -2080,7 +2080,7 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx context.Context, evalC
|
|||||||
}
|
}
|
||||||
|
|
||||||
unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
|
unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
|
||||||
proposedNewVal := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
|
proposedNewVal := objchange.PlannedUnknownObject(schema, unmarkedConfigVal)
|
||||||
proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths)
|
proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths)
|
||||||
|
|
||||||
// Apply detects that the data source will need to be read by the After
|
// Apply detects that the data source will need to be read by the After
|
||||||
@@ -2141,7 +2141,7 @@ func (n *NodeAbstractResourceInstance) planDataSource(ctx context.Context, evalC
|
|||||||
// If we had errors, then we can cover that up by marking the new
|
// If we had errors, then we can cover that up by marking the new
|
||||||
// state as unknown.
|
// state as unknown.
|
||||||
unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
|
unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
|
||||||
newVal = objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
|
newVal = objchange.PlannedUnknownObject(schema, unmarkedConfigVal)
|
||||||
newVal = newVal.MarkWithPaths(configMarkPaths)
|
newVal = newVal.MarkWithPaths(configMarkPaths)
|
||||||
|
|
||||||
// We still want to report the check as failed even if we are still
|
// We still want to report the check as failed even if we are still
|
||||||
@@ -3346,7 +3346,7 @@ func (n *NodeAbstractResourceInstance) deferEphemeralResource(evalCtx EvalContex
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
|
unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
|
||||||
proposedNewVal := objchange.PlannedEphemeralResourceObject(schema, unmarkedConfigVal)
|
proposedNewVal := objchange.PlannedUnknownObject(schema, unmarkedConfigVal)
|
||||||
proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths)
|
proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths)
|
||||||
|
|
||||||
plannedChange = &plans.ResourceInstanceChange{
|
plannedChange = &plans.ResourceInstanceChange{
|
||||||
|
|||||||
@@ -114,17 +114,17 @@ func (p providerForTest) ReadDataSource(_ context.Context, r providers.ReadDataS
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p providerForTest) OpenEphemeralResource(_ context.Context, _ providers.OpenEphemeralResourceRequest) (resp providers.OpenEphemeralResourceResponse) {
|
func (p providerForTest) OpenEphemeralResource(_ context.Context, _ providers.OpenEphemeralResourceRequest) (resp providers.OpenEphemeralResourceResponse) {
|
||||||
//TODO ephemeral - implement me when adding testing support
|
// TODO ephemeral testing support - implement me when adding testing support
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p providerForTest) RenewEphemeralResource(_ context.Context, _ providers.RenewEphemeralResourceRequest) (resp providers.RenewEphemeralResourceResponse) {
|
func (p providerForTest) RenewEphemeralResource(_ context.Context, _ providers.RenewEphemeralResourceRequest) (resp providers.RenewEphemeralResourceResponse) {
|
||||||
//TODO ephemeral - implement me when adding testing support
|
// TODO ephemeral testing support - implement me when adding testing support
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p providerForTest) CloseEphemeralResource(_ context.Context, _ providers.CloseEphemeralResourceRequest) (resp providers.CloseEphemeralResourceResponse) {
|
func (p providerForTest) CloseEphemeralResource(_ context.Context, _ providers.CloseEphemeralResourceRequest) (resp providers.CloseEphemeralResourceResponse) {
|
||||||
//TODO ephemeral - implement me when adding testing support
|
// TODO ephemeral testing support - implement me when adding testing support
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user