-
Notifications
You must be signed in to change notification settings - Fork 101
actions: Implement lifecycle actions and linked resources #1199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
de53cd5
Squashed commit of the following:
austinvalle 3a3fced
initial implementation of linked resources
austinvalle 831006a
quick set add
austinvalle 03d1d43
Merge branch 'main' into av/linked-resources
austinvalle b3515eb
fix tests with extra nil check
austinvalle 4ae7a31
commit non-working
austinvalle 5a247b5
tests tests tests
austinvalle f7b98e3
Merge branch 'main' into av/linked-resources
austinvalle 7951721
identity schema mapping
austinvalle 983fb28
proto5server plan action tests
austinvalle 6b94432
fromproto tests
austinvalle 521519e
toproto tests
austinvalle 6566182
fwserver and tests
austinvalle a319d23
renames
austinvalle 17ab6fd
invoke impl for linked resources
austinvalle 27c5155
refactor linked resource schema retrieval
austinvalle 5ea07ee
fromproto
austinvalle 9a2bf68
toproto and proto6server
austinvalle a096adb
docs
austinvalle 19a6dda
add copyright
austinvalle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,19 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
// TODO:Actions: Eventual package docs for actions | ||
// Package action contains all interfaces, request types, and response | ||
// types for an action implementation. | ||
// | ||
// In Terraform, an action is a concept which enables provider developers | ||
// to offer practitioners ad-hoc side-effects to be used in their configuration. | ||
// Depending on the type of action defined (unlinked, lifecycle, or linked), practitioners | ||
// can trigger actions through the Terraform CLI and as part of the plan / apply lifecycle. | ||
// Actions do not produce any data for practitioners to consume in their configurations, but | ||
// can modify attributes of pre-defined managed resources (referred to as linked resources). | ||
// | ||
// The main starting point for implementations in this package is the | ||
// [Action] type which represents an instance of an action that has its | ||
// own configuration, plan, and invoke logic. The [Action] implementations | ||
// are referenced by the [provider.ProviderWithActions] type Actions method, | ||
// which enables the action practitioner usage. | ||
package action |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package schema | ||
|
||
const ( | ||
// ExecutionOrderInvalid is used to indicate an invalid [ExecutionOrder]. | ||
// Provider developers should not use it. | ||
ExecutionOrderInvalid ExecutionOrder = 0 | ||
|
||
// ExecutionOrderBefore is used to indicate that the action must be invoked before it's | ||
// linked resource's plan/apply. | ||
ExecutionOrderBefore ExecutionOrder = 1 | ||
|
||
// ExecutionOrderAfter is used to indicate that the action must be invoked after it's | ||
// linked resource's plan/apply. | ||
ExecutionOrderAfter ExecutionOrder = 2 | ||
) | ||
|
||
// ExecutionOrder is an enum that represents when an action is invoked relative to it's linked resource. | ||
type ExecutionOrder int32 | ||
|
||
func (d ExecutionOrder) String() string { | ||
switch d { | ||
case 0: | ||
return "Invalid" | ||
case 1: | ||
return "Before" | ||
case 2: | ||
return "After" | ||
} | ||
return "Unknown" | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package schema | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
) | ||
|
||
var _ SchemaType = LifecycleSchema{} | ||
|
||
// LifecycleSchema defines the structure and value types of a lifecycle action. A lifecycle action | ||
// can cause changes to exactly one resource state, defined as a linked resource. | ||
type LifecycleSchema struct { | ||
// ExecutionOrder defines when the lifecycle action must be executed in relation to the linked resource, | ||
// either before or after the linked resource's plan/apply. | ||
ExecutionOrder ExecutionOrder | ||
|
||
// LinkedResource represents the managed resource type that this action can make state changes to. The linked | ||
// resource must be defined in the same provider as the action is defined. | ||
// | ||
// - If the managed resource is built with terraform-plugin-framework, use [LinkedResource]. | ||
// - If the managed resource is built with terraform-plugin-sdk/v2 or the terraform-plugin-go tfprotov5 package, use [RawV5LinkedResource]. | ||
// - If the managed resource is built with the terraform-plugin-go tfprotov6 package, use [RawV6LinkedResource]. | ||
// | ||
// As a lifecycle action can only have a single linked resource, this linked resource data will always be at index 0 | ||
// in the ModifyPlan and Invoke LinkedResources slice. | ||
LinkedResource LinkedResourceType | ||
|
||
// Attributes is the mapping of underlying attribute names to attribute | ||
// definitions. | ||
// | ||
// Names must only contain lowercase letters, numbers, and underscores. | ||
// Names must not collide with any Blocks names. | ||
Attributes map[string]Attribute | ||
|
||
// Blocks is the mapping of underlying block names to block definitions. | ||
// | ||
// Names must only contain lowercase letters, numbers, and underscores. | ||
// Names must not collide with any Attributes names. | ||
Blocks map[string]Block | ||
|
||
// Description is used in various tooling, like the language server, to | ||
// give practitioners more information about what this action is, | ||
// what it's for, and how it should be used. It should be written as | ||
// plain text, with no special formatting. | ||
Description string | ||
|
||
// MarkdownDescription is used in various tooling, like the | ||
// documentation generator, to give practitioners more information | ||
// about what this action is, what it's for, and how it should be | ||
// used. It should be formatted using Markdown. | ||
MarkdownDescription string | ||
|
||
// DeprecationMessage defines warning diagnostic details to display when | ||
// practitioner configurations use this action. The warning diagnostic | ||
// summary is automatically set to "Action Deprecated" along with | ||
// configuration source file and line information. | ||
// | ||
// Set this field to a practitioner actionable message such as: | ||
// | ||
// - "Use examplecloud_do_thing action instead. This action | ||
// will be removed in the next major version of the provider." | ||
// - "Remove this action as it no longer is valid and | ||
// will be removed in the next major version of the provider." | ||
// | ||
DeprecationMessage string | ||
} | ||
|
||
func (s LifecycleSchema) LinkedResourceTypes() []LinkedResourceType { | ||
return []LinkedResourceType{ | ||
s.LinkedResource, | ||
} | ||
} | ||
|
||
func (s LifecycleSchema) isActionSchemaType() {} | ||
|
||
// ApplyTerraform5AttributePathStep applies the given AttributePathStep to the | ||
// schema. | ||
func (s LifecycleSchema) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (any, error) { | ||
return fwschema.SchemaApplyTerraform5AttributePathStep(s, step) | ||
} | ||
|
||
// AttributeAtPath returns the Attribute at the passed path. If the path points | ||
// to an element or attribute of a complex type, rather than to an Attribute, | ||
// it will return an ErrPathInsideAtomicAttribute error. | ||
func (s LifecycleSchema) AttributeAtPath(ctx context.Context, p path.Path) (fwschema.Attribute, diag.Diagnostics) { | ||
return fwschema.SchemaAttributeAtPath(ctx, s, p) | ||
} | ||
|
||
// AttributeAtPath returns the Attribute at the passed path. If the path points | ||
// to an element or attribute of a complex type, rather than to an Attribute, | ||
// it will return an ErrPathInsideAtomicAttribute error. | ||
func (s LifecycleSchema) AttributeAtTerraformPath(ctx context.Context, p *tftypes.AttributePath) (fwschema.Attribute, error) { | ||
return fwschema.SchemaAttributeAtTerraformPath(ctx, s, p) | ||
} | ||
|
||
// GetAttributes returns the Attributes field value. | ||
func (s LifecycleSchema) GetAttributes() map[string]fwschema.Attribute { | ||
return schemaAttributes(s.Attributes) | ||
} | ||
|
||
// GetBlocks returns the Blocks field value. | ||
func (s LifecycleSchema) GetBlocks() map[string]fwschema.Block { | ||
return schemaBlocks(s.Blocks) | ||
} | ||
|
||
// GetDeprecationMessage returns the DeprecationMessage field value. | ||
func (s LifecycleSchema) GetDeprecationMessage() string { | ||
return s.DeprecationMessage | ||
} | ||
|
||
// GetDescription returns the Description field value. | ||
func (s LifecycleSchema) GetDescription() string { | ||
return s.Description | ||
} | ||
|
||
// GetMarkdownDescription returns the MarkdownDescription field value. | ||
func (s LifecycleSchema) GetMarkdownDescription() string { | ||
return s.MarkdownDescription | ||
} | ||
|
||
// GetVersion always returns 0 as action schemas cannot be versioned. | ||
func (s LifecycleSchema) GetVersion() int64 { | ||
return 0 | ||
} | ||
|
||
// Type returns the framework type of the schema. | ||
func (s LifecycleSchema) Type() attr.Type { | ||
return fwschema.SchemaType(s) | ||
} | ||
|
||
// TypeAtPath returns the framework type at the given schema path. | ||
func (s LifecycleSchema) TypeAtPath(ctx context.Context, p path.Path) (attr.Type, diag.Diagnostics) { | ||
return fwschema.SchemaTypeAtPath(ctx, s, p) | ||
} | ||
|
||
// TypeAtTerraformPath returns the framework type at the given tftypes path. | ||
func (s LifecycleSchema) TypeAtTerraformPath(ctx context.Context, p *tftypes.AttributePath) (attr.Type, error) { | ||
return fwschema.SchemaTypeAtTerraformPath(ctx, s, p) | ||
} | ||
|
||
// ValidateImplementation contains logic for validating the provider-defined | ||
// implementation of the schema and underlying attributes and blocks to prevent | ||
// unexpected errors or panics. This logic runs during the GetProviderSchema RPC, | ||
// or via provider-defined unit testing, and should never include false positives. | ||
func (s LifecycleSchema) ValidateImplementation(ctx context.Context) diag.Diagnostics { | ||
var diags diag.Diagnostics | ||
|
||
// TODO:Actions: Implement validation to ensure valid lifecycle "execute" enum and linked resource definitions | ||
|
||
for attributeName, attribute := range s.GetAttributes() { | ||
req := fwschema.ValidateImplementationRequest{ | ||
Name: attributeName, | ||
Path: path.Root(attributeName), | ||
} | ||
|
||
// TODO:Actions: We should confirm with core, but we should be able to remove this next line. | ||
// | ||
// Action schemas define a specific "config" nested block in the action block, which means there | ||
// shouldn't be any conflict with existing or future Terraform core attributes. | ||
diags.Append(fwschema.IsReservedResourceAttributeName(req.Name, req.Path)...) | ||
diags.Append(fwschema.ValidateAttributeImplementation(ctx, attribute, req)...) | ||
} | ||
|
||
for blockName, block := range s.GetBlocks() { | ||
req := fwschema.ValidateImplementationRequest{ | ||
Name: blockName, | ||
Path: path.Root(blockName), | ||
} | ||
|
||
// TODO:Actions: We should confirm with core, but we should be able to remove this next line. | ||
// | ||
// Action schemas define a specific "config" nested block in the action block, which means there | ||
// shouldn't be any conflict with existing or future Terraform core attributes. | ||
diags.Append(fwschema.IsReservedResourceAttributeName(req.Name, req.Path)...) | ||
diags.Append(fwschema.ValidateBlockImplementation(ctx, block, req)...) | ||
} | ||
|
||
return diags | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing not represented in this request that is normally in the plan for resources, is private state.... Should that be in the protocol? Can an action also return new private state? 🤔 (same thought for
InvokeAction
)