opentofu/helper/plugin/grpc_provider_test.go
James Bardin 63dcdbe948 helper/plugin package for grpc servers
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.
2018-10-16 18:58:49 -07:00

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))
}
})
}
}