Skip to content

Conversation

@SarahFrench
Copy link
Member

@SarahFrench SarahFrench commented Dec 12, 2025

Description

Currently the PSS feature is gated by a combination of:

  1. Requiring experiments to be enabled (by building from source or using an alpha release).
  2. Requiring the flag -enable-pluggable-state-storage-experiment=true to be passed to init commands, to choose an experimental version of the command
  3. Requiring the configuration to include the feature's new state_store block.

There is also error feedback telling users when either of 1 or 2 are missing.

This PR changes this, so that the experiment is controlled by configuration instead of a CLI flag.

This makes testing the feature easier, as adding the experiment to the configuration occurs once and can be forgotten. Using the CLI flag requires users to remember it is needed each time they run init, which adds friction and in the past it has been confusing to diagnose errors when the flag is missing.

Now users will need configuration like this (plus an experimental build of TF) to use PSS:

terraform {
  experiments = [pluggable_state_stores]
  # more config here
}

Things to note during review

The flag -enable-pluggable-state-storage-experiment=true was used in the past because Terraform needed to know if the experimental feature should be used early in the init command before configuration was parsed. Flags are present immediately and allow early logic to decide what to do. With the changes in this PR we now need to parse configuration to learn which experiments are named in the configuration. This happens here, before the fork in the logic between old-init behaviour and new-/experimental-init behaviour:

path, err := ModulePath(initArgs.Args)
if err != nil {
diags = diags.Append(err)
view.Diagnostics(diags)
return 1
}
rootMod, rootModDiags := c.loadSingleModuleWithTests(path, initArgs.TestsDirectory)
// We purposefully don't exit early if there are error diagnostics returned here; there are errors related to the Terraform version
// that have precedence and are detected downstream.
// We pass the configuration and diagnostic values from here into downstream code, replacing where the files are parsed there.
// This prevents the diagnostics being lost, as re-parsing the same config results in lost diagnostics.

Note the code comment about why we need to retain the diagnostics returned here, instead of trusting that the same diagnostics will be encountered when files are parsed in downstream logic. We cannot return any errors here because that'll change the return order of different types of errors that are possible in init: "init will error out with an invalid version constraint, even if there are other invalid configuration constructs" (see TestInit_checkRequiredVersionFirst).

When I previously did this:

path, err := ModulePath(initArgs.Args)
if err != nil {
diags = diags.Append(err)
view.Diagnostics(diags)
return 1
}
rootMod, _ := c.loadSingleModuleWithTests(path, initArgs.TestsDirectory)
// We purposefully ignore any diagnostics returned here. They will be encountered downstream,
// when the 'run' logic below is executed. If we return early due to error diagnostics here we
// will break the order that errors are expected to be raised in.

it resulted in test failures due to the ignored diagnostics being lost completely.

Target Release

1.15.x

Rollback Plan

  • If a change needs to be reverted, we will roll out an update to the code within 7 days.

Changes to Security Controls

Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain.

CHANGELOG entry

  • This change is user-facing and I added a changelog entry.
  • This change is not user-facing.

@SarahFrench SarahFrench added the no-changelog-needed Add this to your PR if the change does not require a changelog entry label Dec 12, 2025
Comment on lines 65 to 68
rootMod, _ := c.loadSingleModuleWithTests(path, initArgs.TestsDirectory)
// We purposefully ignore any diagnostics returned here. They will be encountered downstream,
// when the 'run' logic below is executed. If we return early due to error diagnostics here we
// will break the order that errors are expected to be raised in.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a problem with this approach- loading config isn't idempotent.

When you parse HCL files using (p *Parser) ParseHCL from hashicorp/hcl it stores the parsed file in an internal map. Any diagnostics raised from parsing the file the first time are only returned when the file is first parsed. If you re-parse the same file the old parsed data is returned with no associated diagnostics.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This problem was apparent through TestInit_invalidSyntaxBackendAttribute failing - the diagnostics we expect in the test get lost between the 1st and 2nd time the config is parsed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah the hclparse package is clear:

// Any diagnostics for parsing a file are only returned once on the first
// call to parse that file. Callers are expected to collect up diagnostics
// and present them together, so returning diagnostics for the same file
// multiple times would create a confusing result.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've addressed this in 987e8ab

@SarahFrench SarahFrench force-pushed the pss/change-experiment-control branch from 5f53184 to 987e8ab Compare December 12, 2025 18:44
Comment on lines +144 to +148
// TODO(SarahFrench/radeksimko): Once PSS's experiment is over, remove use of arguments and
// restore parsing of config to always happen here in this code instead of calling code.
if rootModEarly == nil {
rootModEarly, earlyConfDiags = c.loadSingleModuleWithTests(path, initArgs.TestsDirectory)
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I don't expect a nil rootModEarly to be passed in, unless there's a future refactor that alters the calling code. Mainly I changed the code here in this way to allow the new method arguments to be used but still leave the appropriate position in the method body marked with that TODO comment.

@SarahFrench SarahFrench force-pushed the pss/change-experiment-control branch 2 times, most recently from 5ce1bd8 to 0865512 Compare December 15, 2025 10:40
@SarahFrench SarahFrench force-pushed the pss/change-experiment-control branch from 1cb8ea2 to b6bb055 Compare December 15, 2025 17:19
…nt `pluggable_state_stores`. This is defined via configuration, instead of the old CLI flag `-enable-pluggable-state-storage-experiment`

This commit removes the old `-enable-pluggable-state-storage-experiment` flag and the equivalent `TF_ENABLE_PLUGGABLE_STATE_STORAGE` ENV. All tests are updated to no longer provide that flag or ENV, and instead all the test fixture configurations are updated to include `experiments = [pluggable_state_stores]`
…state_stores` experiment isn't declared in the config.
Prior to this change the strings.Contains assertion was only receiving a string representing the warning, and the error diagnostic was no longer being asserted against.
…tCommand) Run`, instead pass config and diags into downstream code.
@SarahFrench SarahFrench force-pushed the pss/change-experiment-control branch from b6bb055 to 88c71b7 Compare December 15, 2025 18:23
@SarahFrench SarahFrench marked this pull request as ready for review December 15, 2025 18:35
@SarahFrench SarahFrench requested a review from a team as a code owner December 15, 2025 18:35
@SarahFrench SarahFrench changed the title PSS: Swap to gating the experiment using configuration, instead of CLI flags PSS: Swap to gating the experimental feature using configuration, instead of CLI flags Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog-needed Add this to your PR if the change does not require a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant