Skip to content

Conversation

@SarahFrench
Copy link
Member

This PR allows the experimental PSS version of init logic recognise when there have been no changes since the last init, so init will perform a no-op (in regards to state storage, at least).

I've added a test showing that init is a no-op in those circumstances : TestInit_stateStore_configUnchanged

I've also added tests showing that when backend state and config don't match we hit a not configured yet error diagnostic - see TestInit_stateStore_configChanges. To do this I've created sets of test fixtures where the backend state file and config are not in agreement. A special case of this, upgraded provider versions, is in TestInit_stateStore_providerUpgrade.

Changes covered:

  • state_store config changed - internal/command/testdata/state-store-changed/store-config
  • provider config changed - internal/command/testdata/state-store-changed/provider-config
  • state_store type changed - internal/command/testdata/state-store-changed/state-store-type
  • provider used for PSS changed - internal/command/testdata/state-store-changed/provider-used
  • provider upgraded - internal/command/testdata/state-store-changed/provider-upgraded

Target Release

N/A

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.

…iltin and re-attached providers.

This makes comparison easier when determining if config has changed since last init.
…a change, but handling this scenario isn't implemented yet
Previously dummy values were fine, but as tests using hashes to identify changes these values need to be accurate!
…der version as described in the backend state file fixture for the test.
…as a change, but handling this scenario isn't implemented yet
…ation in the same provider is detected as a change, but handling this scenario isn't implemented yet
…as a change, but handling this scenario isn't implemented yet
…ut handling this scenario isn't implemented yet
Just to avoid any confusion if copy-pasting happens in future.
…ull, and replace dummy hash values with correct values.
@SarahFrench SarahFrench added the no-changelog-needed Add this to your PR if the change does not require a changelog entry label Oct 15, 2025
@SarahFrench SarahFrench marked this pull request as ready for review October 15, 2025 17:08
@SarahFrench SarahFrench requested a review from a team as a code owner October 15, 2025 17:08
radeksimko
radeksimko previously approved these changes Oct 17, 2025
Comment on lines 16 to 18
"hash": 2116468040
},
"hash": 2116468040
Copy link
Member

Choose a reason for hiding this comment

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

Just a random observation - not a blocker - how is it that these two hashes match exactly? I thought that each hash represents a different part of the configuration, so should differ?

Copy link
Member Author

@SarahFrench SarahFrench Oct 17, 2025

Choose a reason for hiding this comment

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

Yeah it's odd, but I double checked and the values are correct.

Using a debugger I found an explanation for why they match. To get hashes of the state_store and provider configs we use the (cty.Value) Hash method (used here in Core code). The (cty.Value) Hash method iterates through values to create a slice of bytes which are used to create the final hash number. The process of building those bytes for the hashes in this use case hits this code, which ignore the names of keys and only use values.

The end result is that, because both the state store and provider have 1 attr with the value "foobar", the bytes used to make the hash in both cases is:

"<"test_store";<"foobar";>;>"

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 I'm half correct - the tuple is constructed in our calling code so we can make the hashes be distinct:

pToHash := cty.TupleVal([]cty.Value{
cty.StringVal(b.Type),
pVal,
})

Copy link
Member Author

@SarahFrench SarahFrench Oct 17, 2025

Choose a reason for hiding this comment

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

I think we should address this, but in a separate PR

Edit: I've since changed this opinion, after we agreed that we should swap to a single hash for the whole state_store block in this discussion.

Copy link
Member Author

@SarahFrench SarahFrench Oct 17, 2025

Choose a reason for hiding this comment

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

All commits following this thread, barring removing unused code, are related to this thread.

… impacted by: state store config, provider config, state store type, provider source
@SarahFrench
Copy link
Member Author

Ah there are still issues...go vet failed me

Comment on lines +207 to +215
toHash := cty.TupleVal([]cty.Value{
cty.StringVal(b.Type), // state store type
ssVal, // state store config

cty.StringVal(b.ProviderAddr.String()), // provider source
cty.StringVal(stateStoreProviderVersion.String()), // provider version
cty.StringVal(b.Provider.Name), // provider name - this is directly parsed from the config, whereas provider source is added separately later after config is parsed.
pVal, // provider config
})
Copy link
Member Author

Choose a reason for hiding this comment

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

Behold the beautiful new hash contents ✨

Copy link
Member

Choose a reason for hiding this comment

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

That's great in terms of comments and clarity 👍🏻 🌟

I am wondering why this is a tuple and why it contains just the values and not the attribute names but I think that is somewhat irrelevant to this PR. Just thinking out loud as I read through the code. 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah this is something that I've copied from the original implementation of hashing for backends and haven't dug into that deeply. I can imagine the reason being that attribute names would be the same each time and not help with producing a unique hash per backend/state_store, and attribute names were less useful for hashing the 2 values from backends, too.

}
},
"hash": 12345
"hash": 0
Copy link
Member

Choose a reason for hiding this comment

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

Why do we persist zero hash and other values if the state store is being removed instead of removing the entire state_store object attribute from here?

Is that for some sort of consistency with how backends behave today?

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 made the hash here zero just to make it really clear that the test fixture doesn't have a valid value for the hash, in case that affects anything in future.

The internal/command/testdata/state-store-to-backend test fixture is intended to be used testing what happens when a user swaps from using a state_store to using a backend. In that scenario the user would have previously run a successful init command, which produces this backend state file. In the intended test scenario, migrating to using a backend, the user would update the config to describe the new backend and then run an init command. This folder represents that in-between state where the config and the backend state file do not match, as the user hasn't run an init command since editing the config.

Does that make sense?

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 made the hash here zero just to make it really clear that the test fixture doesn't have a valid value for the hash, in case that affects anything in future.

This was in response to having a painful experience needing to update test fixtures to have accurate hash values 😅

@SarahFrench SarahFrench merged commit efb2a1c into main Oct 20, 2025
7 checks passed
@SarahFrench SarahFrench deleted the pss/init-base-case-no-config-changes branch October 20, 2025 15:51
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.

2 participants