Files
opentf/internal/command/views/json/change.go
Martin Atkins 4cffff24b1 core: Report reason for deferring data read until apply
We have two different reasons why a data resource might be read only
during apply, rather than during planning as usual: the configuration
contains unknown values, or the data resource as a whole depends on a
managed resource which itself has a change pending.

However, we didn't previously distinguish these two in a way that allowed
the UI to describe the difference, and so we confusingly reported both
as "config refers to values not yet known", which in turn led to a number
of reasonable questions about why Terraform was claiming that but then
immediately below showing the configuration entirely known.

Now we'll use our existing "ActionReason" mechanism to tell the UI layer
which of the two reasons applies to a particular data resource instance.
The "dependency pending" situation tends to happen in conjunction with
"config unknown", so we'll prefer to refer that the configuration is
unknown if both are true.
2022-05-09 11:12:47 -07:00

120 lines
3.8 KiB
Go

package json
import (
"fmt"
"github.com/hashicorp/terraform/internal/plans"
)
func NewResourceInstanceChange(change *plans.ResourceInstanceChangeSrc) *ResourceInstanceChange {
c := &ResourceInstanceChange{
Resource: newResourceAddr(change.Addr),
Action: changeAction(change.Action),
Reason: changeReason(change.ActionReason),
}
if !change.Addr.Equal(change.PrevRunAddr) {
if c.Action == ActionNoOp {
c.Action = ActionMove
}
pr := newResourceAddr(change.PrevRunAddr)
c.PreviousResource = &pr
}
return c
}
type ResourceInstanceChange struct {
Resource ResourceAddr `json:"resource"`
PreviousResource *ResourceAddr `json:"previous_resource,omitempty"`
Action ChangeAction `json:"action"`
Reason ChangeReason `json:"reason,omitempty"`
}
func (c *ResourceInstanceChange) String() string {
return fmt.Sprintf("%s: Plan to %s", c.Resource.Addr, c.Action)
}
type ChangeAction string
const (
ActionNoOp ChangeAction = "noop"
ActionMove ChangeAction = "move"
ActionCreate ChangeAction = "create"
ActionRead ChangeAction = "read"
ActionUpdate ChangeAction = "update"
ActionReplace ChangeAction = "replace"
ActionDelete ChangeAction = "delete"
)
func changeAction(action plans.Action) ChangeAction {
switch action {
case plans.NoOp:
return ActionNoOp
case plans.Create:
return ActionCreate
case plans.Read:
return ActionRead
case plans.Update:
return ActionUpdate
case plans.DeleteThenCreate, plans.CreateThenDelete:
return ActionReplace
case plans.Delete:
return ActionDelete
default:
return ActionNoOp
}
}
type ChangeReason string
const (
ReasonNone ChangeReason = ""
ReasonTainted ChangeReason = "tainted"
ReasonRequested ChangeReason = "requested"
ReasonReplaceTriggeredBy ChangeReason = "replace_triggered_by"
ReasonCannotUpdate ChangeReason = "cannot_update"
ReasonUnknown ChangeReason = "unknown"
ReasonDeleteBecauseNoResourceConfig ChangeReason = "delete_because_no_resource_config"
ReasonDeleteBecauseWrongRepetition ChangeReason = "delete_because_wrong_repetition"
ReasonDeleteBecauseCountIndex ChangeReason = "delete_because_count_index"
ReasonDeleteBecauseEachKey ChangeReason = "delete_because_each_key"
ReasonDeleteBecauseNoModule ChangeReason = "delete_because_no_module"
ReasonReadBecauseConfigUnknown ChangeReason = "read_because_config_unknown"
ReasonReadBecauseDependencyPending ChangeReason = "read_because_dependency_pending"
)
func changeReason(reason plans.ResourceInstanceChangeActionReason) ChangeReason {
switch reason {
case plans.ResourceInstanceChangeNoReason:
return ReasonNone
case plans.ResourceInstanceReplaceBecauseTainted:
return ReasonTainted
case plans.ResourceInstanceReplaceByRequest:
return ReasonRequested
case plans.ResourceInstanceReplaceBecauseCannotUpdate:
return ReasonCannotUpdate
case plans.ResourceInstanceReplaceByTriggers:
return ReasonReplaceTriggeredBy
case plans.ResourceInstanceDeleteBecauseNoResourceConfig:
return ReasonDeleteBecauseNoResourceConfig
case plans.ResourceInstanceDeleteBecauseWrongRepetition:
return ReasonDeleteBecauseWrongRepetition
case plans.ResourceInstanceDeleteBecauseCountIndex:
return ReasonDeleteBecauseCountIndex
case plans.ResourceInstanceDeleteBecauseEachKey:
return ReasonDeleteBecauseEachKey
case plans.ResourceInstanceDeleteBecauseNoModule:
return ReasonDeleteBecauseNoModule
case plans.ResourceInstanceReadBecauseConfigUnknown:
return ReasonReadBecauseConfigUnknown
case plans.ResourceInstanceReadBecauseDependencyPending:
return ReasonReadBecauseDependencyPending
default:
// This should never happen, but there's no good way to guarantee
// exhaustive handling of the enum, so a generic fall back is better
// than a misleading result or a panic
return ReasonUnknown
}
}