PSS: Add parsing of .tfmigrate.hcl files to define state migration operations#38526
PSS: Add parsing of .tfmigrate.hcl files to define state migration operations#38526SarahFrench wants to merge 12 commits intomainfrom
Conversation
e6b00a6 to
7105ee7
Compare
…les including `migrate_from_backend` blocks.
…er` blocks across multiple .tfmigrate.hcl files
…s missing its counterpart `migrate_from_state_store` block
…migrate.hcl files. Add tests for error paths and happy path.
…cross migrate_from_state_store and state_store_provider blocks. When they match, copy provider data across to description of the state store. Copying that data across is similar to how we use methods called `resolveStateStoreProviderType` when paring config (via NewModule or FinalizeConfig functions).
7105ee7 to
657a47f
Compare
…single provider. Also, add early returns to avoid repeated or incorrect error diagnostics. Incorrect errors can occur if an error prevents a block being processed, and then downstream code reacts to the lack of that data with another error. That would be misleading (e.g. an error in a state_store_provider block meaning that later an incorrect error is raised reporting a missing state_store_provider block, when it is incorrect not missing).
SarahFrench
left a comment
There was a problem hiding this comment.
Some self-review/contextual comments to help others with review.
| } else { | ||
| // They match, so copy across relevant data. | ||
| ss.ProviderAddr = ssp.Type | ||
| } |
There was a problem hiding this comment.
This serves a similar purpose as this code in NewModule:
terraform/internal/configs/module.go
Lines 193 to 195 in badc70c
| return file, diags | ||
| } | ||
|
|
||
| func decodeStateStoreProviderBlock(block *hcl.Block) (*RequiredProvider, hcl.Diagnostics) { |
There was a problem hiding this comment.
The logic in this method was adapted from the decodeRequiredProvidersBlock function:
terraform/internal/configs/provider_requirements.go
Lines 33 to 48 in badc70c
In decodeStateStoreProviderBlock we don't have multiple attributes (i.e don't have multiple local names with paired objects). We return early if there isn't 1 entry. After that there's no need for looping but the code looks very similar to that original code.
| // Assert we have a single constraint, for a specific version | ||
| if len(constraints) != 1 { | ||
| diags = append(diags, &hcl.Diagnostic{ | ||
| Severity: hcl.DiagError, | ||
| Summary: "Invalid version constraint", | ||
| Detail: "The version attribute inside the state_store_provider block must specify a single, specific version (e.g. \"= 1.0.0\").", | ||
| Subject: kv.Value.Range().Ptr(), | ||
| }) | ||
| return nil, diags | ||
| } | ||
|
|
||
| // A constraint to use v1.2.3 could have an = operator or no operator at all. | ||
| constraintStr = strings.TrimPrefix(constraintStr, "=") // Remove a preceding `=`, if it exists. | ||
| constraintStr = strings.TrimSpace(constraintStr) // There might have been whitespace between the operator and the version. | ||
|
|
||
| _, err = versions.ParseVersion(constraintStr) | ||
| if err != nil { | ||
| // Errors indicate that the constraint wasn't a specific version. | ||
| diags = append(diags, &hcl.Diagnostic{ | ||
| Severity: hcl.DiagError, | ||
| Summary: `Non-specific version constraint in "state_store_provider" configuration block`, | ||
| Detail: "The version constraint defined in a state_store_provider block must specify a single, specific version (e.g. \"= 1.0.0\", or \"1.0.0\").", | ||
| Subject: kv.Value.Range().Ptr(), | ||
| }) | ||
| return nil, diags | ||
| } |
There was a problem hiding this comment.
Also, I figured it was worth reusing existing types instead of creating a new type that used Version instead of VersionConstraint, so this method returns a RequiredProvider pointer.
Due to this we still use a version constraint, but I opted for validation that asserts the constraint is pinning to an exact value. The hashicorp/go-version representation of the version constraint doesn't allow calling code to inspect data and ask 'is this constraint using an = operator (implied or explicit)?'. Because of that, I landed on this approach where I check for an = operator (implied or explicit) and then attempt to parse the string as a version, and use successful parsing as a sign that the constraint was pinning a single version.
If there are other/better approaches to this I'm keen to get feedback. An alternative is updating hashicorp/go-version but I think that decision shouldn't be taken lightly.
…_store_provider block
…migrate.hcl files
Closes https://hashicorp.atlassian.net/browse/TF-36919
This PR enables parsing .tfmigrate.hcl files and new blocks:
state_store_provider(like required_providers but enforces a single provider and the constraint has to be a specific value)migrate_from_state_store(like a state_store block)migrate_from_backend(like a backend block)This PR also implements validation in the parser like:
migrate_from_state_storeandmigrate_from_backendblocks are mutually exclusive.migrate_from_backendandstate_store_providerare mutually exclusive.migrate_from_state_store,state_store_providermust also be present.migrate_from_state_storeandstate_store_providermust match.state_store_providerblock may only describe a single version (pin to a version, no range of versions) of a single provider (only one entry).The
state migratecommand will eventually use this logic, and this PR hasn't connected those parts of the codebase yet. When that does happen I expect that calling code would populate the ProviderSupplyMode data.Target Release
1.16.x
Rollback Plan
Changes to Security Controls
Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain.
CHANGELOG entry