Skip to content
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

ephemeral: Initial ephemeral resource type implementation #1050

Merged
merged 36 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
639eda9
initial ephemeral resource interfaces
austinvalle Aug 13, 2024
dfd0cdc
add ephemeral resource configure data
austinvalle Aug 13, 2024
fe2b7db
attribute implementations
austinvalle Aug 13, 2024
a88242a
uncomment custom type tests
austinvalle Aug 13, 2024
4e285b7
added block implementations
austinvalle Aug 13, 2024
9b81285
add nested attribute implementations
austinvalle Aug 13, 2024
99fb820
add schema test
austinvalle Aug 13, 2024
745ce70
remove todo
austinvalle Aug 14, 2024
76c9cad
doc updates, renames, removals
austinvalle Aug 27, 2024
1f24856
initial protov5 + fwserver implementation (protov6 stubbed)
austinvalle Aug 28, 2024
ce01e76
Merge branch 'main' into av/ephemeral-resources
austinvalle Aug 29, 2024
07f7437
add fromproto5 tests
austinvalle Aug 29, 2024
0985765
add toproto5 tests
austinvalle Aug 29, 2024
592fc33
add proto5server tests
austinvalle Aug 30, 2024
ec24f33
implement protov6
austinvalle Aug 30, 2024
bda9bbc
schema + metadata tests
austinvalle Aug 30, 2024
c4f94a9
add close proto5/6 tests
austinvalle Aug 30, 2024
bff71bb
add fwserver tests for schema/metadata
austinvalle Sep 3, 2024
9ff4f27
prevent random false positives
austinvalle Sep 3, 2024
56463dc
validate fwserver tests
austinvalle Sep 3, 2024
c7d5ae2
open/renew/close fwserver tests
austinvalle Sep 3, 2024
dce7c87
update error message
austinvalle Sep 4, 2024
b84bb1d
update plugin go
austinvalle Sep 4, 2024
55f2ba6
Merge branch 'main' into av/ephemeral-resources
austinvalle Sep 24, 2024
627a49b
remove `config` from renew
austinvalle Sep 24, 2024
50b570f
remove state from renew/close, add deferred action support
austinvalle Oct 3, 2024
b0a34a9
Merge branch 'main' into av/ephemeral-resources
austinvalle Oct 28, 2024
d2157d1
update doc comments
austinvalle Oct 28, 2024
4443ec0
add experimental note
austinvalle Oct 28, 2024
e21e631
add changelogs
austinvalle Oct 28, 2024
97f6267
initial website documentation
austinvalle Oct 30, 2024
fd91121
build(deps): Bump github.com/hashicorp/terraform-plugin-go (#1051)
dependabot[bot] Oct 30, 2024
7ed7ae2
Merge branch 'main' into av/ephemeral-resources
austinvalle Oct 30, 2024
3340925
Update website/docs/plugin/framework/ephemeral-resources/renew.mdx
austinvalle Oct 30, 2024
9aef978
Update website/docs/plugin/framework/ephemeral-resources/validate-con…
austinvalle Oct 30, 2024
370da65
adjust package docs
austinvalle Oct 30, 2024
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
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20241028-130457.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'provider: Added `ProviderWithEphemeralResources` interface for implementing
ephemeral resources'
time: 2024-10-28T13:04:57.796703-04:00
custom:
Issue: "1050"
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20241028-130618.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'tfsdk: Added `EphemeralResultData` struct for representing ephemeral values
produced by a provider, such as from an ephemeral resource'
time: 2024-10-28T13:06:18.799164-04:00
custom:
Issue: "1050"
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20241028-130758.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'provider: Added `EphemeralResourceData` to `ConfigureResponse`, to pass provider-defined
data to `ephemeral.EphemeralResource` implementations'
time: 2024-10-28T13:07:58.9914-04:00
custom:
Issue: "1050"
5 changes: 5 additions & 0 deletions .changes/unreleased/FEATURES-20241028-130339.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: FEATURES
body: 'ephemeral: New package for implementing ephemeral resources'
time: 2024-10-28T13:03:39.23218-04:00
custom:
Issue: "1050"
5 changes: 5 additions & 0 deletions .changes/unreleased/FEATURES-20241028-130855.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: FEATURES
body: 'ephemeral/schema: New package for implementing ephemeral resource schemas'
time: 2024-10-28T13:08:55.520004-04:00
custom:
Issue: "1050"
6 changes: 6 additions & 0 deletions .changes/unreleased/NOTES-20241028-130308.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: NOTES
body: Ephemeral resource support is in technical preview and offered without compatibility
promises until Terraform 1.10 is generally available.
time: 2024-10-28T13:03:08.373897-04:00
custom:
Issue: "1050"
30 changes: 30 additions & 0 deletions ephemeral/close.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ephemeral

import (
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/privatestate"
)

// CloseRequest represents a request for the provider to close an ephemeral
// resource. An instance of this request struct is supplied as an argument to
// the ephemeral resource's Close function.
type CloseRequest struct {
// Private is provider-defined ephemeral resource private state data
// which was previously provided by the latest Open or Renew operation.
//
// Use the GetKey method to read data.
Private *privatestate.ProviderData
}

// CloseResponse represents a response to a CloseRequest. An
// instance of this response struct is supplied as an argument
// to the ephemeral resource's Close function, in which the provider
// should set values on the CloseResponse as appropriate.
type CloseResponse struct {
// Diagnostics report errors or warnings related to closing the
// resource. An empty slice indicates a successful operation with no
// warnings or errors generated.
Diagnostics diag.Diagnostics
}
28 changes: 28 additions & 0 deletions ephemeral/config_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ephemeral

import "context"

// ConfigValidator describes reusable EphemeralResource configuration validation functionality.
type ConfigValidator interface {
// Description describes the validation in plain text formatting.
//
// This information may be automatically added to ephemeral resource plain text
// descriptions by external tooling.
Description(context.Context) string

// MarkdownDescription describes the validation in Markdown formatting.
//
// This information may be automatically added to ephemeral resource Markdown
// descriptions by external tooling.
MarkdownDescription(context.Context) string

// ValidateEphemeralResource performs the validation.
//
// This method name is separate from the datasource.ConfigValidator
// interface ValidateDataSource method name, provider.ConfigValidator
// interface ValidateProvider method name, and resource.ConfigValidator
// interface ValidateResource method name to allow generic validators.
ValidateEphemeralResource(context.Context, ValidateConfigRequest, *ValidateConfigResponse)
}
33 changes: 33 additions & 0 deletions ephemeral/configure.go
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 ephemeral

import (
"github.com/hashicorp/terraform-plugin-framework/diag"
)

// ConfigureRequest represents a request for the provider to configure an
// ephemeral resource, i.e., set provider-level data or clients. An instance of
// this request struct is supplied as an argument to the EphemeralResource type
// Configure method.
type ConfigureRequest struct {
// ProviderData is the data set in the
// [provider.ConfigureResponse.EphemeralResourceData] field. This data is
// provider-specifc and therefore can contain any necessary remote system
// clients, custom provider data, or anything else pertinent to the
// functionality of the EphemeralResource.
//
// This data is only set after the ConfigureProvider RPC has been called
// by Terraform.
ProviderData any
}

// ConfigureResponse represents a response to a ConfigureRequest. An
// instance of this response struct is supplied as an argument to the
// EphemeralResource type Configure method.
type ConfigureResponse struct {
// Diagnostics report errors or warnings related to configuring of the
// EphemeralResource. An empty slice indicates a successful operation with no
// warnings or errors generated.
Diagnostics diag.Diagnostics
}
50 changes: 50 additions & 0 deletions ephemeral/deferred.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package ephemeral

const (
// DeferredReasonUnknown is used to indicate an invalid `DeferredReason`.
// Provider developers should not use it.
DeferredReasonUnknown DeferredReason = 0

// DeferredReasonEphemeralResourceConfigUnknown is used to indicate that the resource configuration
// is partially unknown and the real values need to be known before the change can be planned.
DeferredReasonEphemeralResourceConfigUnknown DeferredReason = 1

// DeferredReasonProviderConfigUnknown is used to indicate that the provider configuration
// is partially unknown and the real values need to be known before the change can be planned.
DeferredReasonProviderConfigUnknown DeferredReason = 2

// DeferredReasonAbsentPrereq is used to indicate that a hard dependency has not been satisfied.
DeferredReasonAbsentPrereq DeferredReason = 3
)

// Deferred is used to indicate to Terraform that a change needs to be deferred for a reason.
//
// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject
// to change or break without warning. It is not protected by version compatibility guarantees.
type Deferred struct {
// Reason is the reason for deferring the change.
Reason DeferredReason
}

// DeferredReason represents different reasons for deferring a change.
//
// NOTE: This functionality is related to deferred action support, which is currently experimental and is subject
// to change or break without warning. It is not protected by version compatibility guarantees.
type DeferredReason int32

func (d DeferredReason) String() string {
switch d {
case 0:
return "Unknown"
case 1:
return "Ephemeral Resource Config Unknown"
case 2:
return "Provider Config Unknown"
case 3:
return "Absent Prerequisite"
}
return "Unknown"
}
26 changes: 26 additions & 0 deletions ephemeral/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// Package ephemeral contains all interfaces, request types, and response
// types for an ephemeral resource implementation.
//
// In Terraform, an ephemeral resource is a concept which enables provider
// developers to offer practitioners ephemeral values, which will not be stored
// in any artifact produced by Terraform (plan/state). Ephemeral resources can
// optionally implement renewal logic via the (EphemeralResource).Renew method
// and cleanup logic via the (EphemeralResource).Close method.
//
// Ephemeral resources are not saved into the Terraform plan or state and can
// only be referenced in other ephemeral values, such as provider configuration
// attributes. Ephemeral resources are defined by a type/name, such as "examplecloud_thing",
// a schema representing the structure and data types of configuration, and lifecycle logic.
//
// The main starting point for implementations in this package is the
// EphemeralResource type which represents an instance of an ephemeral resource
// that has its own configuration and lifecycle logic. The [ephemeral.EphemeralResource]
// implementations are referenced by the [provider.ProviderWithEphemeralResources] type
// EphemeralResources method, which enables the ephemeral resource practitioner usage.
//
// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until
// these notices are removed.
package ephemeral
108 changes: 108 additions & 0 deletions ephemeral/ephemeral_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ephemeral

import (
"context"
)

// EphemeralResource represents an instance of an ephemeral resource type. This is the core
// interface that all ephemeral resources must implement.
//
// Ephemeral resources can optionally implement these additional concepts:
//
// - Configure: Include provider-level data or clients via EphemeralResourceWithConfigure
//
// - Validation: Schema-based or entire configuration via EphemeralResourceWithConfigValidators
// or EphemeralResourceWithValidateConfig.
//
// - Renew: Handle renewal of an expired remote object via EphemeralResourceWithRenew.
// Ephemeral resources can indicate to Terraform when a renewal must occur via the RenewAt
// response field of the Open/Renew methods. Renew cannot return new result data for the
// ephemeral resource instance, so this logic is only appropriate for remote objects like
// HashiCorp Vault leases, which can be renewed without changing their data.
//
// - Close: Allows providers to clean up the ephemeral resource via EphemeralResourceWithClose.
//
// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until
// these notices are removed.
type EphemeralResource interface {
// Metadata should return the full name of the ephemeral resource, such as
// examplecloud_thing.
Metadata(context.Context, MetadataRequest, *MetadataResponse)

// Schema should return the schema for this ephemeral resource.
Schema(context.Context, SchemaRequest, *SchemaResponse)

// Open is called when the provider must generate a new ephemeral resource. Config values
// should be read from the OpenRequest and new response values set on the OpenResponse.
Open(context.Context, OpenRequest, *OpenResponse)
}

// EphemeralResourceWithRenew is an interface type that extends EphemeralResource to
// include a method which the framework will call when Terraform detects that the
// provider-defined returned RenewAt time for an ephemeral resource has passed. This RenewAt
// response field can be set in the OpenResponse and RenewResponse.
type EphemeralResourceWithRenew interface {
EphemeralResource

// Renew is called when the provider must renew the ephemeral resource based on
// the provided RenewAt time. This RenewAt response field can be set in the OpenResponse and RenewResponse.
//
// Renew cannot return new result data for the ephemeral resource instance, so this logic is only appropriate
// for remote objects like HashiCorp Vault leases, which can be renewed without changing their data.
Renew(context.Context, RenewRequest, *RenewResponse)
}

// EphemeralResourceWithClose is an interface type that extends
// EphemeralResource to include a method which the framework will call when
// Terraform determines that the ephemeral resource can be safely cleaned up.
type EphemeralResourceWithClose interface {
EphemeralResource

// Close is called when the provider can clean up the ephemeral resource.
// Config values may be read from the CloseRequest.
Close(context.Context, CloseRequest, *CloseResponse)
}

// EphemeralResourceWithConfigure is an interface type that extends EphemeralResource to
// include a method which the framework will automatically call so provider
// developers have the opportunity to setup any necessary provider-level data
// or clients in the EphemeralResource type.
type EphemeralResourceWithConfigure interface {
EphemeralResource

// Configure enables provider-level data or clients to be set in the
// provider-defined EphemeralResource type.
Configure(context.Context, ConfigureRequest, *ConfigureResponse)
}

// EphemeralResourceWithConfigValidators is an interface type that extends EphemeralResource to include declarative validations.
//
// Declaring validation using this methodology simplifies implementation of
// reusable functionality. These also include descriptions, which can be used
// for automating documentation.
//
// Validation will include ConfigValidators and ValidateConfig, if both are
// implemented, in addition to any Attribute or Type validation.
type EphemeralResourceWithConfigValidators interface {
EphemeralResource

// ConfigValidators returns a list of functions which will all be performed during validation.
ConfigValidators(context.Context) []ConfigValidator
}

// EphemeralResourceWithValidateConfig is an interface type that extends EphemeralResource to include imperative validation.
//
// Declaring validation using this methodology simplifies one-off
// functionality that typically applies to a single ephemeral resource. Any documentation
// of this functionality must be manually added into schema descriptions.
//
// Validation will include ConfigValidators and ValidateConfig, if both are
// implemented, in addition to any Attribute or Type validation.
type EphemeralResourceWithValidateConfig interface {
EphemeralResource

// ValidateConfig performs the validation.
ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse)
}
23 changes: 23 additions & 0 deletions ephemeral/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package ephemeral

// MetadataRequest represents a request for the EphemeralResource to return metadata,
// such as its type name. An instance of this request struct is supplied as
// an argument to the EphemeralResource type Metadata method.
type MetadataRequest struct {
// ProviderTypeName is the string returned from
// [provider.MetadataResponse.TypeName], if the Provider type implements
// the Metadata method. This string should prefix the EphemeralResource type name
// with an underscore in the response.
ProviderTypeName string
}

// MetadataResponse represents a response to a MetadataRequest. An
// instance of this response struct is supplied as an argument to the
// EphemeralResource type Metadata method.
type MetadataResponse struct {
// TypeName should be the full ephemeral resource type, including the provider
// type prefix and an underscore. For example, examplecloud_thing.
TypeName string
}
Loading
Loading