mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-04 13:17:43 -06:00
63dcdbe948
The new helper/plugin package contains the grpc servers for handling the new plugin protocol The GRPCProviderServer and GRPCProvisionerServer handle the grpc plugin protocol, and convert the requests to the legacy schema.Provider and schema.Provisioner methods.
296 lines
6.3 KiB
Go
296 lines
6.3 KiB
Go
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
"github.com/hashicorp/terraform/plugin/proto"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/msgpack"
|
|
)
|
|
|
|
// The GRPCProviderServer will directly implement the go protobuf server
|
|
var _ proto.ProviderServer = (*GRPCProviderServer)(nil)
|
|
|
|
var (
|
|
typeComparer = cmp.Comparer(cty.Type.Equals)
|
|
valueComparer = cmp.Comparer(cty.Value.RawEquals)
|
|
equateEmpty = cmpopts.EquateEmpty()
|
|
)
|
|
|
|
func TestUpgradeState_jsonState(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 2,
|
|
Schema: map[string]*schema.Schema{
|
|
"two": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
r.StateUpgraders = []schema.StateUpgrader{
|
|
{
|
|
Version: 0,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"zero": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["zero"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("zero not found in %#v", m)
|
|
}
|
|
m["one"] = float64(1)
|
|
delete(m, "zero")
|
|
return m, nil
|
|
},
|
|
},
|
|
{
|
|
Version: 1,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"one": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["one"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("one not found in %#v", m)
|
|
}
|
|
m["two"] = float64(2)
|
|
delete(m, "one")
|
|
return m, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
ResourcesMap: map[string]*schema.Resource{
|
|
"test": r,
|
|
},
|
|
},
|
|
}
|
|
|
|
req := &proto.UpgradeResourceState_Request{
|
|
TypeName: "test",
|
|
Version: 0,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","zero":0}`),
|
|
},
|
|
}
|
|
|
|
resp, err := server.UpgradeResourceState(nil, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(resp.Diagnostics) > 0 {
|
|
for _, d := range resp.Diagnostics {
|
|
t.Errorf("%#v", d)
|
|
}
|
|
t.Fatal("error")
|
|
}
|
|
|
|
val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := cty.ObjectVal(map[string]cty.Value{
|
|
"id": cty.StringVal("bar"),
|
|
"two": cty.NumberIntVal(2),
|
|
})
|
|
|
|
if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
|
|
t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
|
|
}
|
|
}
|
|
|
|
func TestUpgradeState_flatmapState(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 4,
|
|
Schema: map[string]*schema.Schema{
|
|
"four": {
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
// this MigrateState will take the state to version 2
|
|
MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) {
|
|
switch v {
|
|
case 0:
|
|
_, ok := is.Attributes["zero"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("zero not found in %#v", is.Attributes)
|
|
}
|
|
is.Attributes["one"] = "1"
|
|
delete(is.Attributes, "zero")
|
|
fallthrough
|
|
case 1:
|
|
_, ok := is.Attributes["one"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("one not found in %#v", is.Attributes)
|
|
}
|
|
is.Attributes["two"] = "2"
|
|
delete(is.Attributes, "one")
|
|
default:
|
|
return nil, fmt.Errorf("invalid schema version %d", v)
|
|
}
|
|
return is, nil
|
|
},
|
|
}
|
|
|
|
r.StateUpgraders = []schema.StateUpgrader{
|
|
{
|
|
Version: 2,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"two": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["two"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("two not found in %#v", m)
|
|
}
|
|
m["three"] = float64(3)
|
|
delete(m, "two")
|
|
return m, nil
|
|
},
|
|
},
|
|
{
|
|
Version: 3,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"three": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["three"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("three not found in %#v", m)
|
|
}
|
|
m["four"] = float64(4)
|
|
delete(m, "three")
|
|
return m, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
ResourcesMap: map[string]*schema.Resource{
|
|
"test": r,
|
|
},
|
|
},
|
|
}
|
|
|
|
testReqs := []*proto.UpgradeResourceState_Request{
|
|
{
|
|
TypeName: "test",
|
|
Version: 0,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"zero": "0",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 1,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"one": "1",
|
|
},
|
|
},
|
|
},
|
|
// two and up could be stored in flatmap or json states
|
|
{
|
|
TypeName: "test",
|
|
Version: 2,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"two": "2",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 2,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","two":2}`),
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 3,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"three": "3",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 3,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","three":3}`),
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 4,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"four": "4",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 4,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","four":4}`),
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, req := range testReqs {
|
|
t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) {
|
|
resp, err := server.UpgradeResourceState(nil, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(resp.Diagnostics) > 0 {
|
|
for _, d := range resp.Diagnostics {
|
|
t.Errorf("%#v", d)
|
|
}
|
|
t.Fatal("error")
|
|
}
|
|
|
|
val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := cty.ObjectVal(map[string]cty.Value{
|
|
"id": cty.StringVal("bar"),
|
|
"four": cty.NumberIntVal(4),
|
|
})
|
|
|
|
if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
|
|
t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
|
|
}
|
|
})
|
|
}
|
|
}
|