mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Migration of null resource to terraform data (#2481)
Signed-off-by: Ilia Gogotchuri <ilia.gogotchuri0@gmail.com>
This commit is contained in:
parent
7ad0af1f4c
commit
ec4e0cf0e2
@ -17,6 +17,7 @@ ENHANCEMENTS:
|
||||
* State encryption now supports using external programs as key providers. Additionally, the PBKDF2 key provider now supports chaining via the `chain` parameter. ([#2023](https://github.com/opentofu/opentofu/pull/2023))
|
||||
* The `element` function now accepts negative indices, which extends the existing "wrapping" model into the negative direction. In particular, choosing element `-1` selects the final element in the sequence. ([#2371](https://github.com/opentofu/opentofu/pull/2371))
|
||||
* `moved` now supports moving between different types ([#2370](https://github.com/opentofu/opentofu/pull/2370))
|
||||
* `moved` block can now be used to migrate from the `null_resource` to the `terraform_data` resource. ([#2481](https://github.com/opentofu/opentofu/pull/2481))
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
|
@ -169,11 +169,14 @@ func (p *Provider) ImportResourceState(req providers.ImportResourceStateRequest)
|
||||
panic("unimplemented - terraform_remote_state has no resources")
|
||||
}
|
||||
|
||||
// MoveResourceState is called when the state loader encounters an instance state
|
||||
// that has been moved to a new type, and the state should be updated to reflect the change.
|
||||
// This is used to move the old state to the new schema.
|
||||
func (p *Provider) MoveResourceState(r providers.MoveResourceStateRequest) (resp providers.MoveResourceStateResponse) {
|
||||
panic("unimplmented")
|
||||
return moveDataStoreResourceState(r)
|
||||
}
|
||||
|
||||
// ValidateResourceConfig is used to to validate the resource configuration values.
|
||||
// ValidateResourceConfig is used to validate the resource configuration values.
|
||||
func (p *Provider) ValidateResourceConfig(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
|
||||
return validateDataStoreResourceConfig(req)
|
||||
}
|
||||
|
@ -56,6 +56,54 @@ func upgradeDataStoreResourceState(req providers.UpgradeResourceStateRequest) (r
|
||||
return resp
|
||||
}
|
||||
|
||||
// nullResourceSchema returns a schema for a null_resource with relevant attributes for type migration.
|
||||
func nullResourceSchema() providers.Schema {
|
||||
return providers.Schema{
|
||||
Block: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"triggers": {Type: cty.Map(cty.String), Optional: true},
|
||||
"id": {Type: cty.String, Computed: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func moveDataStoreResourceState(req providers.MoveResourceStateRequest) providers.MoveResourceStateResponse {
|
||||
var resp providers.MoveResourceStateResponse
|
||||
if req.SourceTypeName != "null_resource" || req.TargetTypeName != "terraform_data" {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(
|
||||
fmt.Errorf("unsupported move: %s -> %s; only move from null_resource to terraform_data is supported",
|
||||
req.SourceTypeName, req.TargetTypeName))
|
||||
return resp
|
||||
}
|
||||
nullTy := nullResourceSchema().Block.ImpliedType()
|
||||
oldState, err := ctyjson.Unmarshal(req.SourceStateJSON, nullTy)
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
oldStateMap := oldState.AsValueMap()
|
||||
newStateMap := map[string]cty.Value{}
|
||||
|
||||
if trigger, ok := oldStateMap["triggers"]; ok && !trigger.IsNull() {
|
||||
newStateMap["triggers_replace"] = cty.ObjectVal(trigger.AsValueMap())
|
||||
}
|
||||
if id, ok := oldStateMap["id"]; ok && !id.IsNull() {
|
||||
newStateMap["id"] = id
|
||||
}
|
||||
|
||||
currentSchema := dataStoreResourceSchema()
|
||||
newState, err := currentSchema.Block.CoerceValue(cty.ObjectVal(newStateMap))
|
||||
if err != nil {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.TargetState = newState
|
||||
resp.TargetPrivate = req.SourcePrivate
|
||||
return resp
|
||||
}
|
||||
|
||||
func readDataStoreResourceState(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
||||
resp.NewState = req.PriorState
|
||||
return resp
|
||||
|
@ -6,6 +6,7 @@
|
||||
package tf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -82,6 +83,57 @@ func TestManagedDataUpgradeState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagedDataMovedState(t *testing.T) {
|
||||
nullSchema := nullResourceSchema()
|
||||
nullTy := nullSchema.Block.ImpliedType()
|
||||
|
||||
state := cty.ObjectVal(map[string]cty.Value{
|
||||
"triggers": cty.MapVal(map[string]cty.Value{
|
||||
"examplekey": cty.StringVal("value"),
|
||||
}),
|
||||
"id": cty.StringVal("not-quite-unique"),
|
||||
})
|
||||
|
||||
jsState, err := ctyjson.Marshal(state, nullTy)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// empty request should fail
|
||||
req := providers.MoveResourceStateRequest{}
|
||||
|
||||
resp := moveDataStoreResourceState(req)
|
||||
if !resp.Diagnostics.HasErrors() {
|
||||
t.Fatalf("expected error, got %#v", resp)
|
||||
}
|
||||
|
||||
// valid request
|
||||
req = providers.MoveResourceStateRequest{
|
||||
TargetTypeName: "terraform_data",
|
||||
SourceTypeName: "null_resource",
|
||||
SourcePrivate: []byte("PRIVATE"),
|
||||
SourceStateJSON: jsState,
|
||||
}
|
||||
|
||||
resp = moveDataStoreResourceState(req)
|
||||
|
||||
expectedState := cty.ObjectVal(map[string]cty.Value{
|
||||
"triggers_replace": cty.ObjectVal(map[string]cty.Value{
|
||||
"examplekey": cty.StringVal("value"),
|
||||
}),
|
||||
"id": cty.StringVal("not-quite-unique"),
|
||||
"input": cty.NullVal(cty.DynamicPseudoType),
|
||||
"output": cty.NullVal(cty.DynamicPseudoType),
|
||||
})
|
||||
if !resp.TargetState.RawEquals(expectedState) {
|
||||
t.Errorf("prior state was:\n%#v\nmoved state is:\n%#v\n", expectedState, resp.TargetState)
|
||||
}
|
||||
|
||||
if !bytes.Equal(resp.TargetPrivate, req.SourcePrivate) {
|
||||
t.Error("expected private data to be copied")
|
||||
}
|
||||
|
||||
}
|
||||
func TestManagedDataRead(t *testing.T) {
|
||||
req := providers.ReadResourceRequest{
|
||||
TypeName: "terraform_data",
|
||||
|
@ -11,6 +11,7 @@ This page will run you through the most important changes in OpenTofu 1.10:
|
||||
|
||||
- [New features](#new-features)
|
||||
- [New built-in functions](#new-built-in-functions)
|
||||
- [Moved for different resource types](#moved-for-different-resource-types)
|
||||
- [Improvements to existing features](#improvements-to-existing-features)
|
||||
- [External programs as encryption key providers (experimental)](#external-programs-as-encryption-key-providers-experimental)
|
||||
- [Smaller improvements](#smaller-improvements)
|
||||
@ -28,6 +29,11 @@ New builtin provider functions added ([#2306](https://github.com/opentofu/opento
|
||||
- `provider::terraform::encode_tfvars` - Encode an object into a string with the same format as a TFVars file.
|
||||
- `provider::terraform::encode_expr` - Encode an arbitrary expression into a string with valid OpenTofu syntax.
|
||||
|
||||
### Moved for different resource types
|
||||
|
||||
- `moved` block can now be used to migrate between different types of resources ([docs](../language/modules/develop/refactoring.mdx#changing-a-resource-type)).
|
||||
- Builtin provider now supports migration from `null_resource` to `terraform_data` resource.
|
||||
|
||||
## Improvements to existing features
|
||||
|
||||
### External programs as encryption key providers (experimental)
|
||||
|
@ -63,6 +63,7 @@ resource "terraform_data" "bootstrap" {
|
||||
}
|
||||
```
|
||||
|
||||
`moved` block can be used to migrate from the `null_resource` to the `terraform_data` resource. [Migration guide](https://search.opentofu.org/provider/hashicorp/null/latest/docs/guides/terraform-migration)
|
||||
|
||||
## Argument Reference
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user