mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-09 00:42:48 -04:00
what am i doing
This commit is contained in:
parent
f98b7dc092
commit
cd1cece4cb
5 changed files with 161 additions and 116 deletions
|
|
@ -197,7 +197,7 @@ type GetProviderSchemaResponse struct {
|
|||
// StateStores maps the state store type name to that type's schema.
|
||||
StateStores map[string]Schema
|
||||
|
||||
// Actions maps the name of the action to its schema.
|
||||
// Actions maps the type name of the action to its schema.
|
||||
Actions map[string]ActionSchema
|
||||
|
||||
// Diagnostics contains any warnings or errors from the method call.
|
||||
|
|
|
|||
|
|
@ -162,20 +162,6 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
|||
Config: b.Config,
|
||||
},
|
||||
|
||||
&ActionTriggerConfigTransformer{
|
||||
Config: b.Config,
|
||||
Operation: b.Operation,
|
||||
ActionTargets: b.ActionTargets,
|
||||
|
||||
ConcreteActionTriggerNodeFunc: func(node *nodeAbstractActionTrigger, timing RelativeActionTiming) dag.Vertex {
|
||||
return &nodeActionTriggerApplyExpand{
|
||||
nodeAbstractActionTrigger: node,
|
||||
|
||||
relativeTiming: timing,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
&ActionInvokeApplyTransformer{
|
||||
Config: b.Config,
|
||||
Operation: b.Operation,
|
||||
|
|
|
|||
|
|
@ -172,19 +172,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
|||
generateConfigPathForImportTargets: b.GenerateConfigPath,
|
||||
},
|
||||
|
||||
&ActionTriggerConfigTransformer{
|
||||
Config: b.Config,
|
||||
Operation: b.Operation,
|
||||
ActionTargets: b.ActionTargets,
|
||||
queryPlanMode: b.queryPlan,
|
||||
|
||||
ConcreteActionTriggerNodeFunc: func(node *nodeAbstractActionTrigger, _ RelativeActionTiming) dag.Vertex {
|
||||
return &nodeActionTriggerPlanExpand{
|
||||
nodeAbstractActionTrigger: node,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
&ActionInvokePlanTransformer{
|
||||
Config: b.Config,
|
||||
Operation: b.Operation,
|
||||
|
|
|
|||
|
|
@ -461,10 +461,11 @@ func (n *NodeAbstractResource) ActionsProvidedBy() addrs.Map[addrs.ConfigAction,
|
|||
|
||||
for _, at := range n.Config.Managed.ActionTriggers {
|
||||
for _, aRef := range at.Actions {
|
||||
actionCfg, ok := n.ActionConfigs.GetOk(aRef.ConfigAction.Action.InModule(n.ModulePath()))
|
||||
actionCfg, ok := n.ActionConfigs.GetOk(aRef.ConfigAction)
|
||||
if !ok {
|
||||
// this really shouldn't happen, but is it worth a panic?
|
||||
continue
|
||||
// maybe.
|
||||
panic("action config not found!")
|
||||
}
|
||||
|
||||
if actionCfg != nil {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/hashicorp/terraform/internal/genconfig"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/lang/ephemeral"
|
||||
"github.com/hashicorp/terraform/internal/lang/langrefs"
|
||||
"github.com/hashicorp/terraform/internal/moduletest/mocking"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/plans/deferring"
|
||||
|
|
@ -1048,9 +1047,6 @@ func (n *NodePlannableResourceInstance) planActions(ctx EvalContext, change *pla
|
|||
// This includes create triggers for actions such as "createBeforeDelete" (but no delete yet)
|
||||
before, _ := n.triggersForEvent(change.Action)
|
||||
var diags tfdiags.Diagnostics
|
||||
// we don't need to worry about planning actions in configured order, as
|
||||
// long as they are shown to the user and applied in order (we don't get new
|
||||
// planned values from the provider for actions).
|
||||
for _, trigger := range before {
|
||||
if trigger.Condition != nil {
|
||||
condition, condDiags := n.evaluateActionCondition(ctx, trigger)
|
||||
|
|
@ -1062,35 +1058,12 @@ func (n *NodePlannableResourceInstance) planActions(ctx EvalContext, change *pla
|
|||
if !condition {
|
||||
continue
|
||||
}
|
||||
n.planTriggeredActions(ctx, trigger, change.Action)
|
||||
} else {
|
||||
n.planTriggeredActions(trigger.Actions)
|
||||
n.planTriggeredActions(ctx, trigger, change.Action)
|
||||
}
|
||||
}
|
||||
|
||||
// provider, schema, err := getProvider(ctx, n.resolvedProvider)
|
||||
// if err != nil {
|
||||
// diags = diags.Append(&hcl.Diagnostic{
|
||||
// Severity: hcl.DiagError,
|
||||
// Summary: "Failed to get provider",
|
||||
// Detail: fmt.Sprintf("Failed to get provider: %s", err),
|
||||
// Subject: n.lifecycleActionTrigger.invokingSubject,
|
||||
// })
|
||||
|
||||
// return diags
|
||||
// }
|
||||
// actionSchema := schema.Actions[n.actionConfig.Type]
|
||||
|
||||
// expander := ctx.InstanceExpander()
|
||||
// if !expander.AllInstances().HasActionInstance(n.actionAddress) {
|
||||
// diags = diags.Append(&hcl.Diagnostic{
|
||||
// Severity: hcl.DiagError,
|
||||
// Summary: "Reference to non-existent action instance",
|
||||
// Detail: "Action instance was not found in the current context.",
|
||||
// Subject: n.lifecycleActionTrigger.invokingSubject,
|
||||
// })
|
||||
// return diags
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1218,62 +1191,13 @@ func actionIsTriggeredByEvent(events []configs.ActionTriggerEvent, action plans.
|
|||
return triggeredEvents
|
||||
}
|
||||
|
||||
// FIXME: replace this with a more reusable version of evaluateActionCondition from node_action_trigger_instance_plan
|
||||
// @mildwonkey FIXME: replace this with a more reusable version of evaluateActionCondition from node_action_trigger_instance_plan? or something? this is a copy.
|
||||
func (n *NodePlannableResourceInstance) evaluateActionCondition(ctx EvalContext, trigger *configs.ActionTrigger) (bool, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
rd := instances.RepetitionData{}
|
||||
refs, refDiags := langrefs.ReferencesInExpr(addrs.ParseRef, trigger.Condition)
|
||||
diags = diags.Append(refDiags)
|
||||
if diags.HasErrors() {
|
||||
return false, diags
|
||||
}
|
||||
|
||||
for _, ref := range refs {
|
||||
if ref.Subject == addrs.Self {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Self reference not allowed",
|
||||
Detail: `The condition expression cannot reference "self".`,
|
||||
Subject: trigger.Condition.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
return false, diags
|
||||
}
|
||||
|
||||
if containsBeforeEvent(trigger.Events) {
|
||||
// If events contains a before event we want to error if count or each is used
|
||||
for _, ref := range refs {
|
||||
if _, ok := ref.Subject.(addrs.CountAttr); ok {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Count reference not allowed",
|
||||
Detail: `The condition expression cannot reference "count" if the action is run before the resource is applied.`,
|
||||
Subject: trigger.Condition.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
if _, ok := ref.Subject.(addrs.ForEachAttr); ok {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Each reference not allowed",
|
||||
Detail: `The condition expression cannot reference "each" if the action is run before the resource is applied.`,
|
||||
Subject: trigger.Condition.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
return false, diags
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If there are only after events we allow self, count, and each
|
||||
expander := ctx.InstanceExpander()
|
||||
rd = expander.GetResourceInstanceRepetitionData(n.Addr)
|
||||
}
|
||||
// the condition may use count or for_each (but only with after_* actions)
|
||||
expander := ctx.InstanceExpander()
|
||||
rd := expander.GetResourceInstanceRepetitionData(n.Addr)
|
||||
|
||||
scope := ctx.EvaluationScope(nil, nil, rd)
|
||||
val, conditionEvalDiags := scope.EvalExpr(trigger.Condition, cty.Bool)
|
||||
|
|
@ -1296,12 +1220,159 @@ func (n *NodePlannableResourceInstance) evaluateActionCondition(ctx EvalContext,
|
|||
}
|
||||
|
||||
// planTriggeredActions plans and records planned actions for every action listed. The caller is responsible for checking that the action condition is true.
|
||||
func (n *NodePlannableResourceInstance) planTriggeredActions(actionRefs []configs.ActionRef) tfdiags.Diagnostics {
|
||||
func (n *NodePlannableResourceInstance) planTriggeredActions(ctx EvalContext, trigger *configs.ActionTrigger, event plans.Action) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
for _, actionRef := range actionRefs {
|
||||
// get the action config from the action ref
|
||||
fmt.Println(actionRef)
|
||||
for _, actionRef := range trigger.Actions {
|
||||
actionCfg, ok := n.ActionConfigs.GetOk(actionRef.ConfigAction)
|
||||
if !ok {
|
||||
// this should not be possible
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Missing action config in resource",
|
||||
fmt.Sprintf("The Action configuration for %s was not attached to the resource. This is a bug with terraform and should be reported.", actionRef.ConfigAction),
|
||||
))
|
||||
return diags
|
||||
}
|
||||
|
||||
prov, ok := n.resolvedActionProviders.GetOk(actionRef.ConfigAction)
|
||||
if !ok {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Provider not found",
|
||||
Detail: fmt.Sprintf("The provider for action %s was not found in the current context.", actionRef.ConfigAction),
|
||||
Subject: &actionRef.Range,
|
||||
})
|
||||
return diags
|
||||
}
|
||||
|
||||
provider, providerSchema, err := getProvider(ctx, prov)
|
||||
diags = diags.Append(err)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
actionSchema := providerSchema.Actions[actionCfg.Type]
|
||||
|
||||
// The configAction was derived from the ActionRef hcl.Expression referenced inside the resource's lifecycle block, and has not yet been
|
||||
// expanded or fully evaluated, so we will do that now.
|
||||
// Grab the instance key, necessary if the action uses [count.index] or [each.key]
|
||||
repData := instances.RepetitionData{}
|
||||
switch k := n.Addr.Resource.Key.(type) {
|
||||
case addrs.IntKey:
|
||||
repData.CountIndex = k.Value()
|
||||
case addrs.StringKey:
|
||||
repData.EachKey = k.Value()
|
||||
repData.EachValue = cty.DynamicVal
|
||||
}
|
||||
|
||||
ref, evalActionDiags := evaluateActionExpression(actionRef.Expr, repData)
|
||||
diags = append(diags, evalActionDiags...)
|
||||
if diags.HasErrors() {
|
||||
continue
|
||||
}
|
||||
|
||||
// The reference is either an action or action instance
|
||||
var actionAddr addrs.AbsActionInstance
|
||||
switch sub := ref.Subject.(type) {
|
||||
case addrs.Action:
|
||||
actionAddr = sub.Absolute(n.Addr.Module).Instance(addrs.NoKey)
|
||||
case addrs.ActionInstance:
|
||||
actionAddr = sub.Absolute(n.Addr.Module)
|
||||
}
|
||||
|
||||
if !ctx.InstanceExpander().AllInstances().HasActionInstance(actionAddr) {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Reference to non-existent action instance",
|
||||
Detail: "Action instance was not found in the current context.",
|
||||
Subject: &actionRef.Range,
|
||||
})
|
||||
}
|
||||
|
||||
// now evaluate embedded action config using the *action* instance repetition data
|
||||
keyData := ctx.InstanceExpander().GetActionInstanceRepetitionData(actionAddr)
|
||||
configVal := cty.NullVal(actionSchema.ConfigSchema.ImpliedType())
|
||||
if actionCfg.Config != nil {
|
||||
var configDiags tfdiags.Diagnostics
|
||||
configVal, _, configDiags = ctx.EvaluateBlock(actionCfg.Config, actionSchema.ConfigSchema, nil, keyData)
|
||||
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
valDiags := validateResourceForbiddenEphemeralValues(ctx, configVal, actionSchema.ConfigSchema)
|
||||
diags = diags.Append(valDiags.InConfigBody(actionCfg.Config, actionRef.ConfigAction.String()))
|
||||
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
var deprecationDiags tfdiags.Diagnostics
|
||||
configVal, deprecationDiags = ctx.Deprecations().ValidateAndUnmarkConfig(configVal, actionSchema.ConfigSchema, n.ModulePath())
|
||||
diags = diags.Append(deprecationDiags.InConfigBody(actionCfg.Config, actionAddr.String()))
|
||||
}
|
||||
|
||||
// We remove the marks for planning
|
||||
unmarkedConfig, _ := configVal.UnmarkDeepWithPaths()
|
||||
|
||||
cc := ctx.ClientCapabilities()
|
||||
cc.DeferralAllowed = false // for now, deferrals in actions are always disabled
|
||||
resp := provider.PlanAction(providers.PlanActionRequest{
|
||||
ActionType: actionCfg.Type,
|
||||
ProposedActionData: unmarkedConfig,
|
||||
ClientCapabilities: cc,
|
||||
})
|
||||
diags = append(diags, resp.Diagnostics...)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
// i dunno what this is all about
|
||||
// if len(resp.Diagnostics) > 0 {
|
||||
// severity := hcl.DiagWarning
|
||||
// message := "Warnings when planning action"
|
||||
// err := resp.Diagnostics.Warnings().ErrWithWarnings()
|
||||
// if resp.Diagnostics.HasErrors() {
|
||||
// severity = hcl.DiagError
|
||||
// message = "Failed to plan action"
|
||||
// err = resp.Diagnostics.ErrWithWarnings()
|
||||
// }
|
||||
|
||||
// diags = append(diags, tfdiags.Sourceless(
|
||||
// tfdiags.Severity(severity),
|
||||
// ))
|
||||
|
||||
// diags = diags.Append(&hcl.Diagnostic{
|
||||
// Severity: severity,
|
||||
// Summary: message,
|
||||
// Detail: err.Error(),
|
||||
// //Subject: n.lifecycleActionTrigger.invokingSubject, ??? This is really a sourceless issue
|
||||
// })
|
||||
// }
|
||||
if resp.Deferred != nil {
|
||||
// we always set allow_deferrals to be false for actions, so this
|
||||
// should not happen
|
||||
diags = diags.Append(deferring.UnexpectedProviderDeferralDiagnostic(actionAddr))
|
||||
}
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
ai := &plans.ActionInvocationInstance{
|
||||
Addr: actionAddr,
|
||||
ActionTrigger: &plans.ResourceActionTrigger{
|
||||
TriggeringResourceAddr: n.Addr,
|
||||
//ActionTriggerEvent: event,
|
||||
// ugh
|
||||
// i have none of this
|
||||
},
|
||||
ProviderAddr: prov,
|
||||
// we don't get a value back from the provider, so it's up to us to remove the ephemeral values before storing.
|
||||
ConfigValue: ephemeral.RemoveEphemeralValues(configVal),
|
||||
}
|
||||
ctx.Changes().AppendActionInvocation(ai)
|
||||
}
|
||||
|
||||
return diags
|
||||
|
|
|
|||
Loading…
Reference in a new issue