Skip to content

Commit

Permalink
add ephemeral example resource (#264)
Browse files Browse the repository at this point in the history
* eph: add ephemeral resource

Adds a new example ephemeral resource using a required configurable attribute and an example ID.

* tools: bump terraform-plugin-docs

Bump to the latest stable release, as it includes support for ephemeral resource documentation generation.

* docs: add ephemeral resource documentation

* provider: register new example ephemeral resource

* eph: improve alphabetical ordering of struct fields

* eph: use a computed 'value' attribute

To help provider developers easily understand the concept of ephemeral resources, we use a 'value' attribute in the schema, which is set to an example token (token-123).

* docs: update ephemeral resource docs

* provider: create a separate echo provider factory container

Since not every acceptance test requires an ephemeral provider server for the CLI to connect to and interact with, this introduces a separate provider factory container that includes the echo provider.

* eph: add basic acceptance test for ephemeral resource

* docs: rename ephemeral resource configuration file

* eph: improve http response diag
  • Loading branch information
bschaatsbergen authored Jan 13, 2025
1 parent 9c964f7 commit 3cdbd2d
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 32 deletions.
30 changes: 30 additions & 0 deletions docs/ephemeral-resources/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "scaffolding_example Ephemeral Resource - scaffolding"
subcategory: ""
description: |-
Example ephemeral resource
---

# scaffolding_example (Ephemeral Resource)

Example ephemeral resource

## Example Usage

```terraform
ephemeral "scaffolding_example" "example" {
configurable_attribute = "some-value"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `configurable_attribute` (String) Example configurable attribute

### Read-Only

- `value` (String) Example value
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ephemeral "scaffolding_example" "example" {
configurable_attribute = "some-value"
}
77 changes: 77 additions & 0 deletions internal/provider/example_ephemeral_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ ephemeral.EphemeralResource = &ExampleEphemeralResource{}

func NewExampleEphemeralResource() ephemeral.EphemeralResource {
return &ExampleEphemeralResource{}
}

// ExampleEphemeralResource defines the ephemeral resource implementation.
type ExampleEphemeralResource struct {
// client *http.Client // If applicable, a client can be initialized here.
}

// ExampleEphemeralResourceModel describes the ephemeral resource data model.
type ExampleEphemeralResourceModel struct {
ConfigurableAttribute types.String `tfsdk:"configurable_attribute"`
Value types.String `tfsdk:"value"`
}

func (r *ExampleEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_example"
}

func (r *ExampleEphemeralResource) Schema(ctx context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
resp.Schema = schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Example ephemeral resource",

Attributes: map[string]schema.Attribute{
"configurable_attribute": schema.StringAttribute{
MarkdownDescription: "Example configurable attribute",
Required: true, // Ephemeral resources expect their dependencies to already exist.
},
"value": schema.StringAttribute{
Computed: true,
// Sensitive: true, // If applicable, mark the attribute as sensitive.
MarkdownDescription: "Example value",
},
},
}
}

func (r *ExampleEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
var data ExampleEphemeralResourceModel

// Read Terraform config data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

// If applicable, this is a great opportunity to initialize any necessary
// provider client data and make a call using it.
// httpResp, err := r.client.Do(httpReq)
// if err != nil {
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
// return
// }
//
// However, this example hardcodes setting the token attribute to a specific value for brevity.
data.Value = types.StringValue("token-123")

// Save data into ephemeral result data
resp.Diagnostics.Append(resp.Result.Set(ctx, &data)...)
}
48 changes: 48 additions & 0 deletions internal/provider/example_ephemeral_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)

func TestAccExampleEphemeralResource(t *testing.T) {
resource.Test(t, resource.TestCase{
// Ephemeral resources are only available in 1.10 and later
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactoriesWithEcho,
Steps: []resource.TestStep{
{
Config: testAccExampleEphemeralResourceConfig("example"),
ConfigStateChecks: []statecheck.StateCheck{
statecheck.ExpectKnownValue("echo.test", tfjsonpath.New("data").AtMapKey("value"), knownvalue.StringExact("token-123")),
},
},
},
})
}

func testAccExampleEphemeralResourceConfig(configurableAttribute string) string {
return fmt.Sprintf(`
ephemeral "scaffolding_example" "test" {
configurable_attribute = %[1]q
}
provider "echo" {
data = ephemeral.scaffolding_example.test
}
resource "echo" "test" {}
`, configurableAttribute)
}
8 changes: 8 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
"github.com/hashicorp/terraform-plugin-framework/function"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
Expand All @@ -18,6 +19,7 @@ import (
// Ensure ScaffoldingProvider satisfies various provider interfaces.
var _ provider.Provider = &ScaffoldingProvider{}
var _ provider.ProviderWithFunctions = &ScaffoldingProvider{}
var _ provider.ProviderWithEphemeralResources = &ScaffoldingProvider{}

// ScaffoldingProvider defines the provider implementation.
type ScaffoldingProvider struct {
Expand Down Expand Up @@ -72,6 +74,12 @@ func (p *ScaffoldingProvider) Resources(ctx context.Context) []func() resource.R
}
}

func (p *ScaffoldingProvider) EphemeralResources(ctx context.Context) []func() ephemeral.EphemeralResource {
return []func() ephemeral.EphemeralResource{
NewExampleEphemeralResource,
}
}

func (p *ScaffoldingProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewExampleDataSource,
Expand Down
17 changes: 13 additions & 4 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@ import (

"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/echoprovider"
)

// testAccProtoV6ProviderFactories are used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
// testAccProtoV6ProviderFactories is used to instantiate a provider during acceptance testing.
// The factory function is called for each Terraform CLI command to create a provider
// server that the CLI can connect to and interact with.
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"scaffolding": providerserver.NewProtocol6WithError(New("test")()),
}

// testAccProtoV6ProviderFactoriesWithEcho includes the echo provider alongside the scaffolding provider.
// It allows for testing assertions on data returned by an ephemeral resource during Open.
// The echoprovider is used to arrange tests by echoing ephemeral data into the Terraform state.
// This lets the data be referenced in test assertions with state checks.
var testAccProtoV6ProviderFactoriesWithEcho = map[string]func() (tfprotov6.ProviderServer, error){
"scaffolding": providerserver.NewProtocol6WithError(New("test")()),
"echo": echoprovider.NewProviderServer(),
}

func testAccPreCheck(t *testing.T) {
// You can add code here to run prior to any test case execution, for example assertions
// about the appropriate environment variables being set are common to see in a pre-check
Expand Down
17 changes: 9 additions & 8 deletions tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.22.7

require (
github.com/hashicorp/copywrite v0.19.0
github.com/hashicorp/terraform-plugin-docs v0.19.4
github.com/hashicorp/terraform-plugin-docs v0.20.1
)

require (
Expand All @@ -19,7 +19,7 @@ require (
github.com/armon/go-radix v1.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
github.com/bradleyfalzon/ghinstallation/v2 v2.5.0 // indirect
github.com/cli/go-gh v1.2.1 // indirect
github.com/cli/safeexec v1.0.0 // indirect
Expand All @@ -39,14 +39,15 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hc-install v0.7.0 // indirect
github.com/hashicorp/hc-install v0.9.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.21.0 // indirect
github.com/hashicorp/terraform-json v0.22.1 // indirect
github.com/hashicorp/terraform-json v0.23.0 // indirect
github.com/henvic/httpretty v0.0.6 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.15 // indirect
Expand Down Expand Up @@ -77,14 +78,14 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/thanhpk/randstr v1.0.4 // indirect
github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect
github.com/yuin/goldmark v1.7.1 // indirect
github.com/yuin/goldmark v1.7.7 // indirect
github.com/yuin/goldmark-meta v1.1.0 // indirect
github.com/zclconf/go-cty v1.14.4 // indirect
github.com/zclconf/go-cty v1.15.0 // indirect
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
go.mongodb.org/mongo-driver v1.10.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sync v0.10.0 // indirect
Expand Down
Loading

0 comments on commit 3cdbd2d

Please sign in to comment.