stackeval: Populate planned component instance more thoroughly

We'll now track a more realistic Action, the components that the component
instance depends on, and the planned output values.
pull/34505/head
Martin Atkins 2 years ago
parent 524da4f278
commit 25a514f846

@ -8,18 +8,19 @@ import (
"time"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/internal/stacks/stackutils"
"github.com/zclconf/go-cty/cty"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/collections"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/plans/planfile"
"github.com/hashicorp/terraform/internal/plans/planproto"
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
"github.com/hashicorp/terraform/internal/stacks/stackutils"
"github.com/hashicorp/terraform/internal/stacks/tfstackdata1"
"github.com/hashicorp/terraform/internal/states"
)
@ -96,6 +97,12 @@ type PlannedChangeComponentInstance struct {
// are tracked in their own [PlannedChange] objects.
Action plans.Action
// RequiredComponents is a set of the addresses of all of the components
// that provide infrastructure that this one's infrastructure will
// depend on. Any component named here must exist for the entire lifespan
// of this component instance.
RequiredComponents collections.Set[stackaddrs.AbsComponent]
// PlannedInputValues records our best approximation of the component's
// topmost input values during the planning phase. This could contain
// unknown values if one component is configured from results of another.
@ -104,6 +111,8 @@ type PlannedChangeComponentInstance struct {
// with what's captured here.
PlannedInputValues map[string]plans.DynamicValue
PlannedOutputValues map[string]cty.Value
// PlanTimestamp is the timestamp that would be returned from the
// "plantimestamp" function in modules inside this component. We
// must preserve this in the raw plan data to ensure that we can

@ -979,16 +979,46 @@ func (c *ComponentInstance) PlanChanges(ctx context.Context) ([]stackplan.Planne
corePlan, moreDiags := c.CheckModuleTreePlan(ctx)
diags = diags.Append(moreDiags)
if corePlan != nil {
existedBefore := false
if prevState := c.main.PlanPrevState(); prevState != nil {
existedBefore = prevState.HasComponentInstance(c.Addr())
}
destroying := corePlan.UIMode == plans.DestroyMode
refreshOnly := corePlan.UIMode == plans.RefreshOnlyMode
var action plans.Action
switch {
case destroying:
action = plans.Delete
case refreshOnly:
action = plans.Read
case existedBefore:
action = plans.Update
default:
action = plans.Create
}
// FIXME: This is silly because we make ResultValue wrap the output
// values map up into an object and then just unwrap it again
// immediately.
var outputVals map[string]cty.Value
if resultVal := c.ResultValue(ctx, PlanPhase); resultVal.Type().IsObjectType() && resultVal.IsKnown() && !resultVal.IsNull() {
outputVals = make(map[string]cty.Value, resultVal.LengthInt())
for it := resultVal.ElementIterator(); it.Next(); {
k, v := it.Element()
outputVals[k.AsString()] = v
}
}
// We must always at least announce that the component instance exists,
// and that must come before any resource instance changes referring to it.
changes = append(changes, &stackplan.PlannedChangeComponentInstance{
Addr: c.Addr(),
// FIXME: Once we actually have a prior state this should vary
// depending on whether the same component instance existed in
// the prior state.
Action: plans.Create,
PlannedInputValues: corePlan.VariableValues,
Action: action,
RequiredComponents: c.RequiredComponents(ctx),
PlannedInputValues: corePlan.VariableValues,
PlannedOutputValues: outputVals,
// We must remember the plan timestamp so that the plantimestamp
// function can return a consistent result during a later apply phase.

@ -13,6 +13,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/internal/addrs"
terraformProvider "github.com/hashicorp/terraform/internal/builtin/providers/terraform"
"github.com/hashicorp/terraform/internal/collections"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
@ -81,7 +82,11 @@ func TestPlanWithSingleResource(t *testing.T) {
),
Action: plans.Create,
PlannedInputValues: make(map[string]plans.DynamicValue),
PlanTimestamp: fakePlanTimestamp,
PlannedOutputValues: map[string]cty.Value{
"input": cty.StringVal("hello"),
"output": cty.UnknownVal(cty.String),
},
PlanTimestamp: fakePlanTimestamp,
},
&stackplan.PlannedChangeHeader{
TerraformVersion: version.SemVer,
@ -171,7 +176,11 @@ func TestPlanWithSingleResource(t *testing.T) {
},
}
if diff := cmp.Diff(wantChanges, gotChanges, ctydebug.CmpOptions); diff != "" {
cmpOptions := cmp.Options{
ctydebug.CmpOptions,
collections.CmpOptions,
}
if diff := cmp.Diff(wantChanges, gotChanges, cmpOptions); diff != "" {
t.Errorf("wrong changes\n%s", diff)
}
}

@ -41,6 +41,10 @@ func NewState() *State {
}
}
func (s *State) HasComponentInstance(addr stackaddrs.AbsComponentInstance) bool {
return s.componentInstances.HasKey(addr)
}
// AllComponentInstances returns a set of addresses for all of the component
// instances that are tracked in the state.
//

Loading…
Cancel
Save