mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Plannable import: Add generated config to JSON and human-readable plan output (#33154)
* command: keep our promises * remove some nil config checks Remove some of the safety checks that ensure plan nodes have config attached at the appropriate time. * add GeneratedConfig to plan changes objects Add a new GeneratedConfig field alongside Importing in plan changes. * add config generation package The genconfig package implements HCL config generation from provider state values. Thanks to @mildwonkey whose implementation of terraform add is the basis for this package. * generate config during plan If a resource is being imported and does not already have config, attempt to generate that config during planning. The config is generated from the state as an HCL string, and then parsed back into an hcl.Body to attach to the plan graph node. The generated config string is attached to the change emitted by the plan. * complete config generation prototype, and add tests * Plannable import: Add generated config to json and human-readable plan output --------- Co-authored-by: Katy Moe <katy@katy.moe>
This commit is contained in:
parent
79f7f59155
commit
4d837df546
@ -468,6 +468,9 @@ func resourceChangeComment(resource jsonplan.ResourceChange, action plans.Action
|
||||
}
|
||||
if resource.Change.Importing != nil {
|
||||
buf.WriteString(fmt.Sprintf("[bold] # %s[reset] will be imported", dispAddr))
|
||||
if len(resource.Change.GeneratedConfig) > 0 {
|
||||
buf.WriteString("\n #[reset] (config will be written to generated_config.tf)")
|
||||
}
|
||||
printedImported = true
|
||||
break
|
||||
}
|
||||
|
@ -141,6 +141,49 @@ Terraform will perform the following actions:
|
||||
value = "Hello, World!"
|
||||
}
|
||||
|
||||
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
|
||||
`,
|
||||
},
|
||||
"simple_import_with_generated_config": {
|
||||
plan: Plan{
|
||||
ResourceChanges: []jsonplan.ResourceChange{
|
||||
{
|
||||
Address: "test_resource.resource",
|
||||
Mode: "managed",
|
||||
Type: "test_resource",
|
||||
Name: "resource",
|
||||
ProviderName: "test",
|
||||
Change: jsonplan.Change{
|
||||
Actions: []string{"no-op"},
|
||||
Before: marshalJson(t, map[string]interface{}{
|
||||
"id": "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E",
|
||||
"value": "Hello, World!",
|
||||
}),
|
||||
After: marshalJson(t, map[string]interface{}{
|
||||
"id": "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E",
|
||||
"value": "Hello, World!",
|
||||
}),
|
||||
Importing: &jsonplan.Importing{
|
||||
ID: "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E",
|
||||
},
|
||||
GeneratedConfig: `resource "test_resource" "resource" {
|
||||
id = "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E"
|
||||
value = "Hello, World!"
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: `
|
||||
Terraform will perform the following actions:
|
||||
|
||||
# test_resource.resource will be imported
|
||||
# (config will be written to generated_config.tf)
|
||||
resource "test_resource" "resource" {
|
||||
id = "1D5F5E9E-F2E5-401B-9ED5-692A215AC67E"
|
||||
value = "Hello, World!"
|
||||
}
|
||||
|
||||
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
|
||||
`,
|
||||
},
|
||||
|
@ -132,6 +132,14 @@ type Change struct {
|
||||
// of the Importing struct is subject to change, so downstream consumers
|
||||
// should treat any values in here as strictly optional.
|
||||
Importing *Importing `json:"importing,omitempty"`
|
||||
|
||||
// GeneratedConfig contains any HCL config generated for this resource
|
||||
// during planning as a string.
|
||||
//
|
||||
// If this is populated, then Importing should also be populated but this
|
||||
// might change in the future. However, nNot all Importing changes will
|
||||
// contain generated config.
|
||||
GeneratedConfig string `json:"generated_config,omitempty"`
|
||||
}
|
||||
|
||||
// Importing is a nested object for the resource import metadata.
|
||||
@ -465,6 +473,7 @@ func MarshalResourceChanges(resources []*plans.ResourceInstanceChangeSrc, schema
|
||||
AfterSensitive: json.RawMessage(afterSensitive),
|
||||
ReplacePaths: replacePaths,
|
||||
Importing: importing,
|
||||
GeneratedConfig: rc.GeneratedConfig,
|
||||
}
|
||||
|
||||
if rc.DeposedKey != states.NotDeposed {
|
||||
|
@ -14,6 +14,7 @@ func NewResourceInstanceChange(change *plans.ResourceInstanceChangeSrc) *Resourc
|
||||
Resource: newResourceAddr(change.Addr),
|
||||
Action: changeAction(change.Action),
|
||||
Reason: changeReason(change.ActionReason),
|
||||
GeneratedConfig: change.GeneratedConfig,
|
||||
}
|
||||
|
||||
// The order here matters, we want the moved action to take precedence over
|
||||
@ -51,6 +52,7 @@ type ResourceInstanceChange struct {
|
||||
Action ChangeAction `json:"action"`
|
||||
Reason ChangeReason `json:"reason,omitempty"`
|
||||
Importing *Importing `json:"importing,omitempty"`
|
||||
GeneratedConfig string `json:"generated_config,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ResourceInstanceChange) String() string {
|
||||
|
Loading…
Reference in New Issue
Block a user