Skip to content

Commit 0eca06f

Browse files
committed
add website docs for ephemeral resources
1 parent da63d74 commit 0eca06f

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed

website/data/plugin-testing-nav-data.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@
160160
{
161161
"title": "Terraform Configuration",
162162
"path": "acceptance-tests/configuration"
163+
},
164+
{
165+
"title": "Ephemeral Resources",
166+
"path": "acceptance-tests/ephemeral-resources"
163167
}
164168
]
165169
},
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
---
2+
page_title: 'Plugin Development - Acceptance Testing: Ephemeral Resources'
3+
description: >-
4+
Guidance on how to test ephemeral resources and data.
5+
---
6+
7+
<Highlight>
8+
9+
Ephemeral resource support is in technical preview and offered without compatibility promises until Terraform 1.10 is generally available.
10+
11+
</Highlight>
12+
13+
# Ephemeral Resources
14+
15+
[Ephemeral Resources](/terraform/language/v1.10.x/resources/ephemeral) are an abstraction that allows Terraform to reference external data, similar to [data sources](/terraform/language/data-sources), without persisting that data to plan or state artifacts. The `terraform-plugin-testing` module exclusively uses Terraform plan and state artifacts for it's assertion-based test checks, like [plan checks](/terraform/plugin/testing/acceptance-tests/plan-checks) or [state checks](/terraform/plugin/testing/acceptance-tests/state-checks), which means that ephemeral resource data cannot be asserted using these methods alone.
16+
17+
In addition to this, ephemeral resources have a different [lifecycle](/terraform/language/v1.10.x/resources/ephemeral#lifecycle) then resources or data sources and must be [referenced](/terraform/language/v1.10.x/resources/ephemeral#referencing-ephemeral-resources) as a dependency to a managed resource or data source to be invoked. This means for acceptance testing ephemeral resource logic, provider developers should use a real world example where the ephemeral resource would be referenced.
18+
19+
The following is a test for a hypothetical `examplecloud_secret` ephemeral resource which is referenced by a provider configuration that has a single managed resource. For this test to pass, the ephemeral `examplecloud_secret` resource must return valid data, specifically a kerberos `username`, `password`, and `realm`, which are used to configure the `dns` provider and create a DNS record.
20+
21+
```go
22+
func TestExampleCloudSecret_DnsKerberos(t *testing.T) {
23+
resource.UnitTest(t, resource.TestCase{
24+
// Ephemeral resources are only available in 1.10 and later
25+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
26+
tfversion.SkipBelow(tfversion.Version1_10_0),
27+
},
28+
ExternalProviders: map[string]resource.ExternalProvider{
29+
"dns": {
30+
Source: "hashicorp/dns",
31+
},
32+
},
33+
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){
34+
"examplecloud": providerserver.NewProtocol5WithError(New()),
35+
},
36+
Steps: []resource.TestStep{
37+
{
38+
Config: `
39+
# Retrieves a secret containing user kerberos configuration
40+
ephemeral "examplecloud_secret" "krb" {
41+
name = "example_kerberos_user"
42+
}
43+
44+
# The usage of this provider and managed resource will indicate to Terraform
45+
# that the "ephemeral.examplecloud_secret.krb" resource is needed.
46+
provider "dns" {
47+
update {
48+
server = "ns.example.com"
49+
gssapi {
50+
realm = ephemeral.examplecloud_secret.krb.secret_data.realm
51+
username = ephemeral.examplecloud_secret.krb.secret_data.username
52+
password = ephemeral.examplecloud_secret.krb.secret_data.password
53+
}
54+
}
55+
}
56+
57+
# Successful creation of this resource indicates a success!
58+
resource "dns_a_record_set" "record_set" {
59+
zone = "example.com."
60+
addresses = [
61+
"192.168.0.1",
62+
"192.168.0.2",
63+
"192.168.0.3",
64+
]
65+
}
66+
`,
67+
},
68+
},
69+
})
70+
}
71+
```
72+
73+
See the Terraform [ephemeral documentation](http://localhost:3000/terraform/language/v1.10.x/resources/ephemeral#referencing-ephemeral-resources) for more details on where ephemeral data can be referenced in configurations.
74+
75+
## Testing ephemeral data with `echo` provider
76+
77+
Test assertions on [result data](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/ephemeral#OpenResponse.Result) returned by an ephemeral resource during [`Open`](/terraform/plugin/framework/ephemeral-resources/open) can be arranged using the `echoprovider` package.
78+
79+
This package contains a [Protocol V6 Terraform Provider](/terraform/plugin/terraform-plugin-protocol#protocol-version-6) named `echo`, with a single managed resource also named `echo`. Using the `echo` provider configuration and an instance of the managed resource, ephemeral data can be "echoed" from the provider configuration into Terraform state, where it can be referenced in test assertions with [state checks](/terraform/plugin/testing/acceptance-tests/state-checks). For example:
80+
81+
```terraform
82+
ephemeral "examplecloud_secret" "krb" {
83+
name = "example_kerberos_user"
84+
}
85+
86+
provider "echo" {
87+
# Provide the ephemeral data we want to run test assertions against
88+
data = ephemeral.examplecloud_secret.krb.secret_data
89+
}
90+
91+
# The ephemeral data will be echoed into state
92+
resource "echo" "test_krb" {}
93+
```
94+
95+
<Highlight>
96+
97+
This provider is designed specifically to be used as a utility for acceptance testing ephemeral data and is only available via the `terraform-plugin-testing` Go module.
98+
99+
</Highlight>
100+
101+
### Using `echo` provider in acceptance tests
102+
103+
First, we include the `echo` provider using the [`echoprovider.NewProviderServer`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/echoprovider#NewProviderServer) function in the `(TestCase).ProtoV6ProviderFactories` property:
104+
105+
```go
106+
import (
107+
// .. other imports
108+
109+
"github.com/hashicorp/terraform-plugin-testing/echoprovider"
110+
)
111+
112+
func TestExampleCloudSecret(t *testing.T) {
113+
resource.UnitTest(t, resource.TestCase{
114+
// Ephemeral resources are only available in 1.10 and later
115+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
116+
tfversion.SkipBelow(tfversion.Version1_10_0),
117+
},
118+
// Include the provider we want to test: `examplecloud`
119+
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){
120+
"examplecloud": providerserver.NewProtocol5WithError(New()),
121+
},
122+
// Include `echo` as a v6 provider from `terraform-plugin-testing`
123+
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
124+
"echo": echoprovider.NewProviderServer(),
125+
},
126+
Steps: []resource.TestStep{
127+
// .. test step configurations can now use the `echo` and `examplecloud` providers
128+
},
129+
})
130+
}
131+
```
132+
133+
After including both providers, our test step `Config` references the ephemeral data from `examplecloud_secret` in the `echo` provider configuration `data` attribute:
134+
135+
```go
136+
func TestExampleCloudSecret(t *testing.T) {
137+
resource.UnitTest(t, resource.TestCase{
138+
// .. test case setup from previous step
139+
140+
Steps: []resource.TestStep{
141+
{
142+
Config: `
143+
ephemeral "examplecloud_secret" "krb" {
144+
name = "example_kerberos_user"
145+
}
146+
147+
provider "echo" {
148+
data = ephemeral.examplecloud_secret.krb.secret_data
149+
}
150+
151+
resource "echo" "test_krb" {}
152+
`,
153+
},
154+
},
155+
})
156+
}
157+
```
158+
159+
The `echo.test_krb` managed resource has a single computed `data` attribute, which will contain the provider configuration `data` results. This data is then used in assertions with the [state check](/terraform/plugin/testing/acceptance-tests/state-checks) functionality:
160+
161+
```go
162+
func TestExampleCloudSecret(t *testing.T) {
163+
resource.UnitTest(t, resource.TestCase{
164+
// .. test case setup from previous step
165+
166+
Steps: []resource.TestStep{
167+
{
168+
Config: `
169+
ephemeral "examplecloud_secret" "krb" {
170+
name = "example_kerberos_user"
171+
}
172+
173+
provider "echo" {
174+
data = ephemeral.examplecloud_secret.krb.secret_data
175+
}
176+
177+
resource "echo" "test_krb" {}
178+
`,
179+
ConfigStateChecks: []statecheck.StateCheck{
180+
statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("realm"), knownvalue.StringExact("EXAMPLE.COM")),
181+
statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("username"), knownvalue.StringExact("john-doe")),
182+
statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("password"), knownvalue.StringRegexp(regexp.MustCompile(`^.{12}$`))),
183+
},
184+
},
185+
},
186+
})
187+
}
188+
```
189+
190+
`data` is a `dynamic` attribute, so whatever [type](/terraform/language/expressions/types) you pass in will be directly reflected in the managed resource `data` attribute. In the config above, we reference an object (`secret_data`) from the ephemeral resource instance, so the resulting type of `echo.test_krb.data` is also an `object`.
191+
192+
You can also reference the entire ephemeral resource instance for assertions, rather than specific attributes:
193+
194+
```go
195+
func TestExampleCloudSecret(t *testing.T) {
196+
resource.UnitTest(t, resource.TestCase{
197+
// .. test case setup from previous step
198+
199+
Steps: []resource.TestStep{
200+
{
201+
Config: `
202+
ephemeral "examplecloud_secret" "krb" {
203+
name = "example_kerberos_user"
204+
}
205+
206+
provider "echo" {
207+
data = ephemeral.examplecloud_secret.krb
208+
}
209+
210+
resource "echo" "test_krb" {}
211+
`,
212+
ConfigStateChecks: []statecheck.StateCheck{
213+
statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("name"), knownvalue.StringExact("example_kerberos_user")),
214+
},
215+
},
216+
},
217+
})
218+
}
219+
```

0 commit comments

Comments
 (0)