-
Notifications
You must be signed in to change notification settings - Fork 10.4k
PSS: Add parsing of .tfmigrate.hcl files to define state migration operations
#38526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
52058f5
feat: Parsing a configuration directory can include .tfmigrate.hcl fi…
SarahFrench 869c169
feat: Add ability to parse `state_store_provider` blocks from .tfmigr…
SarahFrench 868a455
feat: Block using both `migrate_from_backend` and `state_store_provid…
SarahFrench 1ceba3e
feat: Add validation that catches if a `state_store_provider` block i…
SarahFrench 278b0b1
feat: Add ability to parse `migrate_from_state_store` blocks from .tf…
SarahFrench cbf5a4f
refactor: Avoid potentially nil `mod` variable
SarahFrench 657a47f
fix: Add validation checks to ensure the same provider is described a…
SarahFrench 45b0ec8
feat: Add validation that blocks .tfmigrate.hcl files being present b…
SarahFrench 984ad8d
refactor: Replace if...else blocks with switch statement for clarity.
SarahFrench 9880c07
feat: Add validation that state_store_provider blocks contain only a …
SarahFrench 6c093df
test: Add test case demonstrating parsing of objects inside the state…
SarahFrench c7b58cb
test: Reorganise and group test cases for error scenarios parsing .tf…
SarahFrench d52a776
refactor: Parse version attr value as version and then make constrain…
SarahFrench 21f402f
Respond to reviewer feedback
SarahFrench 6686dbb
Change language to use from blocks, with nested state_store or backed…
SarahFrench e38eaff
refactor: StateMigrationInstructions field names simplified + use emb…
SarahFrench 13948f8
test: Update tests to assert diagnostic sources that reference from b…
SarahFrench File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,6 +28,7 @@ const ( | |||||||
| // MatchTestFiles option, or from the default test directory. | ||||||||
| // If this option is not specified, test files will not be loaded. | ||||||||
| // Query files (.tfquery.hcl) are also loaded from the given directory. | ||||||||
| // State Migration files (.tfmigrate.hcl) are also loaded from the given directory. | ||||||||
| // | ||||||||
| // If this method returns nil, that indicates that the given directory does not | ||||||||
| // exist at all or could not be opened for some reason. Callers may wish to | ||||||||
|
|
@@ -79,6 +80,76 @@ func (p *Parser) LoadConfigDir(path string, opts ...Option) (*Module, hcl.Diagno | |||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
| // Check if we need to load state migration files | ||||||||
| if len(fileSet.StateMigrations) > 0 { | ||||||||
| stateMigrationFiles, fDiags := p.loadStateMigrateFiles(path, fileSet.StateMigrations) | ||||||||
| diags = append(diags, fDiags...) | ||||||||
| // If there are errors they may be duplicated below, so return early. | ||||||||
| // We return an incomplete module representation. | ||||||||
| if diags.HasErrors() { | ||||||||
| mod.SourceDir = path | ||||||||
|
SarahFrench marked this conversation as resolved.
|
||||||||
| return mod, diags | ||||||||
| } | ||||||||
|
|
||||||||
| if mod != nil { | ||||||||
|
SarahFrench marked this conversation as resolved.
Outdated
|
||||||||
| mod.StateMigrationInstructions = &StateMigrationInstructions{} | ||||||||
| for _, smf := range stateMigrationFiles { | ||||||||
| diags = diags.Extend(mod.appendStateMigrationFile(smf)) | ||||||||
| } | ||||||||
|
|
||||||||
| // If there are errors that might raise false positive below, so return early. | ||||||||
| // We return an incomplete module representation. | ||||||||
| if diags.HasErrors() { | ||||||||
| mod.SourceDir = path | ||||||||
| return mod, diags | ||||||||
| } | ||||||||
|
|
||||||||
| // Now, we perform some final checks that can only be done once all .tfmigrate.hcl files are loaded. | ||||||||
| // Note: Other checks, like mutual exclusivity, were already performed when parsing single files or appending files. | ||||||||
| ssp := mod.StateMigrationInstructions.StateStoreProvider | ||||||||
| ss := mod.StateMigrationInstructions.MigrateFromStateStore | ||||||||
| b := mod.StateMigrationInstructions.MigrateFromBackend | ||||||||
| switch { | ||||||||
| case ssp == nil && ss == nil && b == nil: | ||||||||
| // Files present but all empty | ||||||||
| diags = diags.Append(&hcl.Diagnostic{ | ||||||||
| Severity: hcl.DiagError, | ||||||||
| Summary: `Empty state migration configuration`, | ||||||||
| Detail: `The configuration includes .tfmigrate.hcl files, but they are empty. Please make sure they include the necessary blocks to define a state migration, or remove the files from your project.`, | ||||||||
| }) | ||||||||
| case ss != nil && ssp == nil: | ||||||||
| // Missing state_store_provider block | ||||||||
| diags = diags.Append(&hcl.Diagnostic{ | ||||||||
| Severity: hcl.DiagError, | ||||||||
| Summary: `Missing "state_store_provider" block for state store migration`, | ||||||||
| Detail: `The configuration includes a "migrate_from_state_store" block but is missing the required "state_store_provider" block. Add a "state_store_provider" block to specify the provider to use when migrating state out of that state store.`, | ||||||||
| }) | ||||||||
| case ss == nil && ssp != nil: | ||||||||
| // Missing migrate_from_state_store block | ||||||||
| diags = diags.Append(&hcl.Diagnostic{ | ||||||||
| Severity: hcl.DiagError, | ||||||||
| Summary: `Missing "migrate_from_state_store" block for state store migration`, | ||||||||
| Detail: `The configuration includes a "state_store_provider" block but is missing the required "migrate_from_state_store" block. Add a "migrate_from_state_store" block to specify the state store to migrate from.`, | ||||||||
| }) | ||||||||
| case ss != nil && ssp != nil: | ||||||||
| // Both migrate_from_state_store and state_store_provider blocks are present, | ||||||||
| // but are they in agreement with each other? | ||||||||
| if ss.Provider.Name != ssp.Name { | ||||||||
| diags = diags.Append(&hcl.Diagnostic{ | ||||||||
| Severity: hcl.DiagError, | ||||||||
| Summary: `Inconsistent provider information for state migration`, | ||||||||
| Detail: fmt.Sprintf(`The configuration's "state_store_provider" block defines a provider called %q but the "migrate_from_state_store" block uses a provider called %q instead. Please update the blocks so that they are in agreement.`, | ||||||||
| ssp.Name, | ||||||||
| ss.Provider.Name, | ||||||||
| ), | ||||||||
| }) | ||||||||
| } else { | ||||||||
| // They match, so copy across relevant data. | ||||||||
| ss.ProviderAddr = ssp.Type | ||||||||
| } | ||||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This serves a similar purpose as this code in NewModule: terraform/internal/configs/module.go Lines 193 to 195 in badc70c
|
||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| if mod != nil { | ||||||||
| mod.SourceDir = path | ||||||||
|
|
@@ -220,6 +291,19 @@ func (p *Parser) loadQueryFiles(basePath string, paths []string) ([]*QueryFile, | |||||||
| return files, diags | ||||||||
| } | ||||||||
|
|
||||||||
| func (p *Parser) loadStateMigrateFiles(basePath string, paths []string) ([]*StateMigrationFile, hcl.Diagnostics) { | ||||||||
| var diags hcl.Diagnostics | ||||||||
|
|
||||||||
| files := make([]*StateMigrationFile, 0, len(paths)) | ||||||||
| for _, path := range paths { | ||||||||
| f, fDiags := p.LoadStateMigrationFile(path) | ||||||||
| diags = append(diags, fDiags...) | ||||||||
| files = append(files, f) | ||||||||
| } | ||||||||
|
|
||||||||
| return files, diags | ||||||||
| } | ||||||||
|
|
||||||||
| // fileExt returns the Terraform configuration extension of the given | ||||||||
| // path, or a blank string if it is not a recognized extension. | ||||||||
| func fileExt(path string) string { | ||||||||
|
|
||||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.