Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 12 additions & 149 deletions tfprotov5/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,12 @@ type ActionServer interface {
ValidateActionConfig(context.Context, *ValidateActionConfigRequest) (*ValidateActionConfigResponse, error)

// PlanAction is called when Terraform is attempting to
// calculate a plan for an action. Depending on the type defined in
// the action schema, Terraform may also pass the plan of linked resources
// that the action can modify or return unmodified to influence Terraform's plan.
// calculate a plan for an action.
PlanAction(context.Context, *PlanActionRequest) (*PlanActionResponse, error)

// InvokeAction is called when Terraform wants to execute the logic of an action.
// Depending on the type defined in the action schema, Terraform may also pass the
// state of linked resources. The provider runs the logic of the action, reporting progress
// events as desired, then sends a final complete event that has the linked resource's resulting
// state and identity.
// The provider runs the logic of the action, reporting progress
// events as desired, then sends a final complete event.
//
// If an error occurs, the provider sends a complete event with the relevant diagnostics.
InvokeAction(context.Context, *InvokeActionRequest) (*InvokeActionServerStream, error)
Expand All @@ -57,30 +53,6 @@ type ValidateActionConfigRequest struct {
// from knowing the value at request time. Any attributes not directly
// set in the configuration will be null.
Config *DynamicValue

// LinkedResources contains the configuration data of the managed resource types that are linked to this action.
//
// - If the action schema type is Unlinked, this field will be empty.
LinkedResources []*LinkedResourceConfig
}

// LinkedResourceConfig represents linked resource config data used in the ValidateActionConfig RPC.
type LinkedResourceConfig struct {
// TypeName is the type of linked resource Terraform is validating.
TypeName string

// Config is the configuration the user supplied for the linked resource. See
// the documentation on `DynamicValue` for more information about
// safely accessing the configuration.
//
// The configuration is represented as a tftypes.Object, with each
// attribute and nested block getting its own key and value.
//
// This configuration may contain unknown values if a user uses
// interpolation or other functionality that would prevent Terraform
// from knowing the value at request time. Any attributes not directly
// set in the configuration will be null.
Config *DynamicValue
}

// ValidateActionConfigResponse is the response from the provider about
Expand All @@ -98,11 +70,6 @@ type PlanActionRequest struct {
// ActionType is the name of the action being called.
ActionType string

// LinkedResources contains the data of the managed resource types that are linked to this action.
//
// - If the action schema type is Unlinked, this field will be empty.
LinkedResources []*ProposedLinkedResource

// Config is the configuration the user supplied for the action. See
// the documentation on `DynamicValue` for more information about
// safely accessing the configuration.
Expand All @@ -113,43 +80,9 @@ type PlanActionRequest struct {
ClientCapabilities *PlanActionClientCapabilities
}

// ProposedLinkedResource represents linked resource data before PlanAction is called.
type ProposedLinkedResource struct {
// PriorState is the state of the linked resource before the plan is applied,
// represented as a `DynamicValue`. See the documentation for
// `DynamicValue` for information about safely accessing the state.
PriorState *DynamicValue

// PlannedState is the latest indication of what the state for the
// linked resource should be after apply, represented as a `DynamicValue`.
// See the documentation for `DynamicValue` for information about safely
// accessing the planned state.
//
// Since PlannedState is the most recent plan for the linked resource, it could
// be the result of an RPC call to PlanResourceChange or an RPC call to PlanAction
// for a predecessor action
PlannedState *DynamicValue

// Config is the configuration the user supplied for the linked resource. See
// the documentation on `DynamicValue` for more information about
// safely accessing the configuration.
Config *DynamicValue

// PriorIdentity is the identity of the resource before the plan is
// applied, represented as a `ResourceIdentityData`.
PriorIdentity *ResourceIdentityData
}

// PlanActionResponse is the response from the provider when planning an action. If the action
// has linked resources, it will contain any modifications made to the planned state or identity.
// PlanActionResponse is the response from the provider when planning an action.
type PlanActionResponse struct {
// LinkedResources contains the provider modified data of the managed resource types that are linked to this action.
//
// This field is not currently in-use, but future action schema types will use this field to plan modifications of state data for linked resources.
LinkedResources []*PlannedLinkedResource

// Diagnostics report errors or warnings related to plannning the action and calculating
// the planned state of the requested linked resources. Returning an empty slice
// Diagnostics report errors or warnings related to planning the action. Returning an empty slice
// indicates a successful validation with no warnings or errors generated.
Diagnostics []*Diagnostic

Expand All @@ -158,61 +91,20 @@ type PlanActionResponse struct {
Deferred *Deferred
}

// PlannedLinkedResource represents linked resource data that was planned during PlanAction and returned.
type PlannedLinkedResource struct {
// PlannedState is the provider's indication of what the state for the
// linked resource should be after apply, represented as a `DynamicValue`. See
// the documentation for `DynamicValue` for information about safely
// creating the `DynamicValue`.
PlannedState *DynamicValue

// PlannedIdentity is the provider's indication of what the identity for the
// linked resource should be after apply, represented as a `ResourceIdentityData`
PlannedIdentity *ResourceIdentityData
}

// InvokeActionRequest is the request Terraform sends when it wants to execute
// the logic of an action.
type InvokeActionRequest struct {
// ActionType is the name of the action being called.
ActionType string

// LinkedResources contains the data of the managed resource types that are linked to this action.
//
// - If the action schema type is Unlinked, this field will be empty.
LinkedResources []*InvokeLinkedResource

// Config is the configuration the user supplied for the action. See
// the documentation on `DynamicValue` for more information about
// safely accessing the configuration.
Config *DynamicValue
}

// InvokeLinkedResource represents linked resource data before InvokeAction is called.
type InvokeLinkedResource struct {
// PriorState is the state of the linked resource before changes are applied,
// represented as a `DynamicValue`. See the documentation for
// `DynamicValue` for information about safely accessing the state.
PriorState *DynamicValue

// PlannedState is the latest indication of what the state for the
// linked resource should look like after changes are applied, represented
// as a `DynamicValue`. See the documentation for `DynamicValue` for
// information about safely accessing the planned state.
//
// Since PlannedState is the most recent state for the linked resource, it could
// be the result of an RPC call to ApplyResourceChange or an RPC call to InvokeAction
// for a predecessor action.
PlannedState *DynamicValue

// Config is the configuration the user supplied for the linked resource. See
// the documentation on `DynamicValue` for more information about
// safely accessing the configuration.
Config *DynamicValue

// PlannedIdentity is Terraform's plan for what the linked resource identity should
// look like after the changes are applied, represented as a `ResourceIdentityData`.
PlannedIdentity *ResourceIdentityData
// ClientCapabilities defines optionally supported protocol features for the
// InvokeAction RPC, such as forward-compatible Terraform behavior changes.
ClientCapabilities *InvokeActionClientCapabilities
}

// InvokeActionServerStream represents a streaming response to an
Expand All @@ -222,8 +114,8 @@ type InvokeLinkedResource struct {
type InvokeActionServerStream struct {
// Events is the iterator that the provider can stream progress messages back to Terraform
// as the action is executing. Once the provider has completed the action invocation, the provider must
// respond with a completed event with the new linked resource state or diagnostics explaining why
// the action failed.
// respond with a completed event. If the action failed, the completed event must contain
// diagnostics explaining why the action failed.
Events iter.Seq[InvokeActionEvent]
}

Expand Down Expand Up @@ -255,42 +147,13 @@ type ProgressInvokeActionEventType struct {

func (a ProgressInvokeActionEventType) isInvokeActionEventType() {}

// CompletedInvokeActionEventType represents the final completed event, along with all of the linked resource
// data modified by the provider or diagnostics about an action invocation failure.
// CompletedInvokeActionEventType represents the final completed event, along with
// potential diagnostics about an action invocation failure.
type CompletedInvokeActionEventType struct {
// LinkedResources contains the provider modified data of the managed resource types that are linked to this action.
//
// This field is not currently in-use, but future action schema types will use this field to modify state data for linked resources.
LinkedResources []*NewLinkedResource

// Diagnostics report errors or warnings related to invoking an action.
// Returning an empty slice indicates a successful invocation with no warnings
// or errors generated.
Diagnostics []*Diagnostic
}

func (a CompletedInvokeActionEventType) isInvokeActionEventType() {}

// NewLinkedResource represents linked resource data that was changed during InvokeAction and returned.
//
// Depending on how the action was invoked, the modified state data will either be immediately recorded in
// state or reconcicled in a future terraform apply operation.
type NewLinkedResource struct {
// NewState is the provider's understanding of what the linked resource's
// state is after changes are applied, represented as a `DynamicValue`.
// See the documentation for `DynamicValue` for information about
// safely creating the `DynamicValue`.
//
// Any attribute, whether computed or not, that has a known value in
// the PlannedState in the InvokeActionRequest must be preserved
// exactly as it was in NewState.
NewState *DynamicValue

// NewIdentity is the provider's understanding of what the linked resource's
// identity is after changes are applied, represented as a `ResourceIdentityData`.
NewIdentity *ResourceIdentityData

// RequiresReplace can only be set if diagnostics are returned for the action and indicate
// the linked resource must be replaced as a result of the action invocation error.
RequiresReplace bool
}
27 changes: 1 addition & 26 deletions tfprotov5/action_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,8 @@

package tfprotov5

// ActionSchema is how Terraform defines the shape of action data and
// how the practitioner can interact with the action.
// ActionSchema is how Terraform defines the shape of action data.
type ActionSchema struct {
// Schema is the definition for the action data itself, which will be specified in an action block in the user's configuration.
Schema *Schema

// Type defines how a practitioner can trigger an action, as well as what effect the action can have on the state
// of the linked managed resources. There is currently only one type of action supported:
// - Unlinked actions are actions that cannot cause changes to resource states.
Type ActionSchemaType
}

// ActionSchemaType is an intentionally unimplementable interface that
// functions as an enum, allowing us to use different strongly-typed action schema types
// that contain additional, but different data, as a generic "action" type.
//
// An action can currently only be one type (Unlinked), which is statically defined in the protocol. Future action types
// will be added to the protocol first, then implemented in this package.
type ActionSchemaType interface {
isActionSchemaType()
}

var (
_ ActionSchemaType = UnlinkedActionSchemaType{}
)

// UnlinkedActionSchemaType represents an unlinked action, which cannot cause changes to resource states.
type UnlinkedActionSchemaType struct{}

func (a UnlinkedActionSchemaType) isActionSchemaType() {}
9 changes: 9 additions & 0 deletions tfprotov5/client_capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,12 @@ type PlanActionClientCapabilities struct {
// handle deferred responses from the provider.
DeferralAllowed bool
}

// InvokeActionClientCapabilities allows Terraform to publish information
// regarding optionally supported protocol features for the InvokeAction RPC,
// such as forward-compatible Terraform behavior changes.
//
// Maintainer Note: This is in the protocol in Terraform Core,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice note, answered my question 😆

// but currently they are not sending any capabilities for this RPC.
type InvokeActionClientCapabilities struct {
}
55 changes: 5 additions & 50 deletions tfprotov5/internal/fromproto/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,79 +14,34 @@ func ValidateActionConfigRequest(in *tfplugin5.ValidateActionConfig_Request) *tf
}

return &tfprotov5.ValidateActionConfigRequest{
ActionType: in.ActionType,
Config: DynamicValue(in.Config),
LinkedResources: LinkedResourceConfigs(in.LinkedResources),
ActionType: in.ActionType,
Config: DynamicValue(in.Config),
}
}

func LinkedResourceConfigs(in []*tfplugin5.LinkedResourceConfig) []*tfprotov5.LinkedResourceConfig {
resp := make([]*tfprotov5.LinkedResourceConfig, 0, len(in))

for _, inLinkedResource := range in {
resp = append(resp, &tfprotov5.LinkedResourceConfig{
TypeName: inLinkedResource.TypeName,
Config: DynamicValue(inLinkedResource.Config),
})
}

return resp
}

func PlanActionRequest(in *tfplugin5.PlanAction_Request) *tfprotov5.PlanActionRequest {
if in == nil {
return nil
}

resp := &tfprotov5.PlanActionRequest{
ActionType: in.ActionType,
LinkedResources: ProposedLinkedResources(in.LinkedResources),
Config: DynamicValue(in.Config),
ClientCapabilities: PlanActionClientCapabilities(in.ClientCapabilities),
}

return resp
}

func ProposedLinkedResources(in []*tfplugin5.PlanAction_Request_LinkedResource) []*tfprotov5.ProposedLinkedResource {
resp := make([]*tfprotov5.ProposedLinkedResource, 0, len(in))

for _, inLinkedResource := range in {
resp = append(resp, &tfprotov5.ProposedLinkedResource{
PriorState: DynamicValue(inLinkedResource.PriorState),
PlannedState: DynamicValue(inLinkedResource.PlannedState),
Config: DynamicValue(inLinkedResource.Config),
PriorIdentity: ResourceIdentityData(inLinkedResource.PriorIdentity),
})
}

return resp
}

func InvokeActionRequest(in *tfplugin5.InvokeAction_Request) *tfprotov5.InvokeActionRequest {
if in == nil {
return nil
}

resp := &tfprotov5.InvokeActionRequest{
ActionType: in.ActionType,
LinkedResources: InvokeLinkedResources(in.LinkedResources),
Config: DynamicValue(in.Config),
}

return resp
}

func InvokeLinkedResources(in []*tfplugin5.InvokeAction_Request_LinkedResource) []*tfprotov5.InvokeLinkedResource {
resp := make([]*tfprotov5.InvokeLinkedResource, 0, len(in))

for _, inLinkedResource := range in {
resp = append(resp, &tfprotov5.InvokeLinkedResource{
PriorState: DynamicValue(inLinkedResource.PriorState),
PlannedState: DynamicValue(inLinkedResource.PlannedState),
Config: DynamicValue(inLinkedResource.Config),
PlannedIdentity: ResourceIdentityData(inLinkedResource.PlannedIdentity),
})
ActionType: in.ActionType,
Config: DynamicValue(in.Config),
ClientCapabilities: InvokeActionClientCapabilities(in.ClientCapabilities),
}

return resp
Expand Down
Loading
Loading