@@ -15,6 +15,7 @@ import (
1515 "github.com/hashicorp/terraform/internal/e2e"
1616 "github.com/hashicorp/terraform/internal/getproviders"
1717 "github.com/hashicorp/terraform/internal/plans"
18+ "github.com/hashicorp/terraform/internal/states/statefile"
1819 "github.com/zclconf/go-cty/cty"
1920)
2021
@@ -249,6 +250,85 @@ func TestPrimary_stateStore(t *testing.T) {
249250 }
250251 t .Parallel ()
251252
253+ tf := e2e .NewBinary (t , terraformBin , "testdata/full-workflow-with-state-store-fs" )
254+
255+ // In order to test integration with PSS we need a provider plugin implementing a state store.
256+ // Here will build the simple6 (built with protocol v6) provider, which implements PSS.
257+ simple6Provider := filepath .Join (tf .WorkDir (), "terraform-provider-simple6" )
258+ simple6ProviderExe := e2e .GoBuild ("github.com/hashicorp/terraform/internal/provider-simple-v6/main" , simple6Provider )
259+
260+ // Move the provider binaries into a directory that we will point terraform
261+ // to using the -plugin-dir cli flag.
262+ platform := getproviders .CurrentPlatform .String ()
263+ hashiDir := "cache/registry.terraform.io/hashicorp/"
264+ if err := os .MkdirAll (tf .Path (hashiDir , "simple6/0.0.1/" , platform ), os .ModePerm ); err != nil {
265+ t .Fatal (err )
266+ }
267+ if err := os .Rename (simple6ProviderExe , tf .Path (hashiDir , "simple6/0.0.1/" , platform , "terraform-provider-simple6" )); err != nil {
268+ t .Fatal (err )
269+ }
270+
271+ //// INIT
272+ stdout , stderr , err := tf .Run ("init" , "-enable-pluggable-state-storage-experiment=true" , "-plugin-dir=cache" , "-no-color" )
273+ if err != nil {
274+ t .Fatalf ("unexpected init error: %s\n stderr:\n %s" , err , stderr )
275+ }
276+
277+ if ! strings .Contains (stdout , "Terraform created an empty state file for the default workspace" ) {
278+ t .Errorf ("notice about creating the default workspace is missing from init output:\n %s" , stdout )
279+ }
280+
281+ //// PLAN
282+ // No separate plan step; this test lets the apply make a plan.
283+
284+ //// APPLY
285+ stdout , stderr , err = tf .Run ("apply" , "-auto-approve" , "-no-color" )
286+ if err != nil {
287+ t .Fatalf ("unexpected apply error: %s\n stderr:\n %s" , err , stderr )
288+ }
289+
290+ if ! strings .Contains (stdout , "Resources: 1 added, 0 changed, 0 destroyed" ) {
291+ t .Errorf ("incorrect apply tally; want 1 added:\n %s" , stdout )
292+ }
293+
294+ // Check the statefile saved by the fs state store.
295+ path := "terraform.tfstate.d/default/terraform.tfstate"
296+ f , err := tf .OpenFile (path )
297+ if err != nil {
298+ t .Fatalf ("unexpected error opening state file %s: %s\n stderr:\n %s" , path , err , stderr )
299+ }
300+ defer f .Close ()
301+
302+ stateFile , err := statefile .Read (f )
303+ if err != nil {
304+ t .Fatalf ("unexpected error reading statefile %s: %s\n stderr:\n %s" , path , err , stderr )
305+ }
306+
307+ r := stateFile .State .RootModule ().Resources
308+ if len (r ) != 1 {
309+ t .Fatalf ("expected state to include one resource, but got %d" , len (r ))
310+ }
311+ if _ , ok := r ["terraform_data.my-data" ]; ! ok {
312+ t .Fatalf ("expected state to include terraform_data.my-data but it's missing" )
313+ }
314+ }
315+
316+ // Requires TF_TEST_EXPERIMENTS to be set in the environment
317+ func TestPrimary_stateStore_inMem (t * testing.T ) {
318+ if v := os .Getenv ("TF_TEST_EXPERIMENTS" ); v == "" {
319+ t .Skip ("can't run without enabling experiments in the executable terraform binary, enable with TF_TEST_EXPERIMENTS=1" )
320+ }
321+
322+ if ! canRunGoBuild {
323+ // We're running in a separate-build-then-run context, so we can't
324+ // currently execute this test which depends on being able to build
325+ // new executable at runtime.
326+ //
327+ // (See the comment on canRunGoBuild's declaration for more information.)
328+ t .Skip ("can't run without building a new provider executable" )
329+ }
330+ t .Parallel ()
331+
252332 tf := e2e .NewBinary (t , terraformBin , "testdata/full-workflow-with-state-store-inmem" )
253333
254334 // In order to test integration with PSS we need a provider plugin implementing a state store.
@@ -268,6 +348,9 @@ func TestPrimary_stateStore(t *testing.T) {
268348 }
269349
270350 //// INIT
351+ //
352+ // Note - the inmem PSS implementation means that the default workspace state created during init
353+ // is lost as soon as the command completes.
271354 stdout , stderr , err := tf .Run ("init" , "-enable-pluggable-state-storage-experiment=true" , "-plugin-dir=cache" , "-no-color" )
272355 if err != nil {
273356 t .Fatalf ("unexpected init error: %s\n stderr:\n %s" , err , stderr )
@@ -281,6 +364,9 @@ func TestPrimary_stateStore(t *testing.T) {
281364 // No separate plan step; this test lets the apply make a plan.
282365
283366 //// APPLY
367+ //
368+ // Note - the inmem PSS implementation means that writing to the default workspace during apply
369+ // is creating the default state file for the first time.
284370 stdout , stderr , err = tf .Run ("apply" , "-auto-approve" , "-no-color" )
285371 if err != nil {
286372 t .Fatalf ("unexpected apply error: %s\n stderr:\n %s" , err , stderr )
0 commit comments