move conversion functions into separate package
Managing which function need to be shared between the terraform plugin and the helper plugin without creating cycles was becoming difficult. Move all functions related to converting between terraform and proto type into plugin/convert.
This commit is contained in:
parent
88bfbeb9c5
commit
c07ce1cd4b
|
@ -1,41 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
)
|
||||
|
||||
// diagsFromWarnsErrs converts the warnings and errors return by the lagacy
|
||||
// provider to diagnostics.
|
||||
func diagsFromWarnsErrs(warns []string, errs []error) (diags []*proto.Diagnostic) {
|
||||
for _, w := range warns {
|
||||
diags = appendDiag(diags, w)
|
||||
}
|
||||
|
||||
for _, e := range errs {
|
||||
diags = appendDiag(diags, e)
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
// appendDiag appends a new diagnostic from a warning string or an error. This
|
||||
// panics if d is not a string or error.
|
||||
func appendDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic {
|
||||
switch d := d.(type) {
|
||||
case error:
|
||||
diags = append(diags, &proto.Diagnostic{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: d.Error(),
|
||||
})
|
||||
case string:
|
||||
diags = append(diags, &proto.Diagnostic{
|
||||
Severity: proto.Diagnostic_WARNING,
|
||||
Summary: d,
|
||||
})
|
||||
case *proto.Diagnostic:
|
||||
diags = append(diags, d)
|
||||
case []*proto.Diagnostic:
|
||||
diags = append(diags, d...)
|
||||
}
|
||||
return diags
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
)
|
||||
|
||||
func TestDiagnostics(t *testing.T) {
|
||||
diags := diagsFromWarnsErrs(
|
||||
[]string{
|
||||
"warning 1",
|
||||
"warning 2",
|
||||
},
|
||||
[]error{
|
||||
errors.New("error 1"),
|
||||
errors.New("error 2"),
|
||||
},
|
||||
)
|
||||
|
||||
expected := []*proto.Diagnostic{
|
||||
{
|
||||
Severity: proto.Diagnostic_WARNING,
|
||||
Summary: "warning 1",
|
||||
},
|
||||
{
|
||||
Severity: proto.Diagnostic_WARNING,
|
||||
Summary: "warning 2",
|
||||
},
|
||||
{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: "error 1",
|
||||
},
|
||||
{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: "error 2",
|
||||
},
|
||||
}
|
||||
|
||||
if !cmp.Equal(expected, diags) {
|
||||
t.Fatal(cmp.Diff(expected, diags))
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/plugin/convert"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
@ -42,20 +43,20 @@ func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProvider
|
|||
}
|
||||
|
||||
resp.Provider = &proto.Schema{
|
||||
Block: protoSchemaBlock(s.getProviderSchemaBlock()),
|
||||
Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()),
|
||||
}
|
||||
|
||||
for typ, res := range s.provider.ResourcesMap {
|
||||
resp.ResourceSchemas[typ] = &proto.Schema{
|
||||
Version: int64(res.SchemaVersion),
|
||||
Block: protoSchemaBlock(res.CoreConfigSchema()),
|
||||
Block: convert.ConfigSchemaToProto(res.CoreConfigSchema()),
|
||||
}
|
||||
}
|
||||
|
||||
for typ, dat := range s.provider.DataSourcesMap {
|
||||
resp.DataSourceSchemas[typ] = &proto.Schema{
|
||||
Version: int64(dat.SchemaVersion),
|
||||
Block: protoSchemaBlock(dat.CoreConfigSchema()),
|
||||
Block: convert.ConfigSchemaToProto(dat.CoreConfigSchema()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,14 +84,14 @@ func (s *GRPCProviderServer) ValidateProviderConfig(_ context.Context, req *prot
|
|||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
|
||||
warns, errs := s.provider.Validate(config)
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, diagsFromWarnsErrs(warns, errs))
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -102,14 +103,14 @@ func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *
|
|||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
|
||||
warns, errs := s.provider.ValidateResource(req.TypeName, config)
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, diagsFromWarnsErrs(warns, errs))
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -121,14 +122,14 @@ func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *pr
|
|||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
|
||||
warns, errs := s.provider.ValidateResource(req.TypeName, config)
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, diagsFromWarnsErrs(warns, errs))
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -148,7 +149,7 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
if req.RawState.Json != nil {
|
||||
err = json.Unmarshal(req.RawState.Json, &jsonMap)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
if req.RawState.Flatmap != nil {
|
||||
jsonMap, version, err = s.upgradeFlatmapState(version, req.RawState.Flatmap, res)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +167,7 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
// complete the upgrade of the JSON states
|
||||
jsonMap, err = s.upgradeJSONState(version, jsonMap, res)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -174,14 +175,14 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
|
|||
// that it can be re-decoded using the actual schema.
|
||||
val, err := schema.JSONMapToStateValue(jsonMap, block)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// encode the final state to the expected msgpack format
|
||||
newStateMP, err := msgpack.Marshal(val, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -299,13 +300,13 @@ func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_R
|
|||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||
err = s.provider.Configure(config)
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -318,7 +319,7 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
|
||||
stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -326,7 +327,7 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
|
||||
newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -335,13 +336,13 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
|
|||
|
||||
newConfigVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newConfigMP, err := msgpack.Marshal(newConfigVal, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -360,13 +361,13 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -381,20 +382,20 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
|
||||
diff, err := s.provider.Diff(info, priorState, config)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// now we need to apply the diff to the prior state, so get the planned state
|
||||
plannedStateVal, err := schema.ApplyDiff(priorStateVal, diff, block)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedMP, err := msgpack.Marshal(plannedStateVal, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.PlannedState.Msgpack = plannedMP
|
||||
|
@ -402,7 +403,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
// the Meta field gets encoded into PlannedPrivate
|
||||
plannedPrivate, err := json.Marshal(diff.Meta)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.PlannedPrivate = plannedPrivate
|
||||
|
@ -418,7 +419,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
|
|||
|
||||
requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -438,13 +439,13 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
|
||||
priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -456,38 +457,38 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
|
|||
|
||||
var private map[string]interface{}
|
||||
if err := json.Unmarshal(req.PlannedPrivate, &private); err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
diff, err := schema.DiffFromValues(priorStateVal, plannedStateVal, res)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newInstanceState, err := s.provider.Apply(info, priorState, diff)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.NewState.Msgpack = newStateMP
|
||||
|
||||
meta, err := json.Marshal(newInstanceState.Meta)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.Private = meta
|
||||
|
@ -506,7 +507,7 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
|||
|
||||
newInstanceStates, err := s.provider.ImportState(info, req.Id)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -516,19 +517,19 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
|
|||
|
||||
newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
meta, err := json.Marshal(is.Meta)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -555,7 +556,7 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -569,26 +570,26 @@ func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDa
|
|||
// the old behavior
|
||||
diff, err := s.provider.ReadDataDiff(info, config)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// now we can get the new complete data source
|
||||
newInstanceState, err := s.provider.ReadDataApply(info, diff)
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
newStateMP, err := msgpack.Marshal(newStateVal, block.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
resp.State.Msgpack = newStateMP
|
||||
|
|
|
@ -4,10 +4,11 @@ import (
|
|||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/plugin/convert"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
ctyconvert "github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
context "golang.org/x/net/context"
|
||||
)
|
||||
|
@ -33,7 +34,7 @@ func (s *GRPCProvisionerServer) GetSchema(_ context.Context, req *proto.GetProvi
|
|||
resp := &proto.GetProvisionerSchema_Response{}
|
||||
|
||||
resp.Provisioner = &proto.Schema{
|
||||
Block: protoSchemaBlock(schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()),
|
||||
Block: convert.ConfigSchemaToProto(schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
@ -46,14 +47,14 @@ func (s *GRPCProvisionerServer) ValidateProvisionerConfig(_ context.Context, req
|
|||
|
||||
configVal, err := msgpack.Unmarshal(req.Config.Msgpack, cfgSchema.ImpliedType())
|
||||
if err != nil {
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, err)
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
config := terraform.NewResourceConfigShimmed(configVal, cfgSchema)
|
||||
|
||||
warns, errs := s.provisioner.Validate(config)
|
||||
resp.Diagnostics = appendDiag(resp.Diagnostics, diagsFromWarnsErrs(warns, errs))
|
||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
@ -74,7 +75,7 @@ func stringMapFromValue(val cty.Value) map[string]string {
|
|||
continue
|
||||
}
|
||||
|
||||
av, _ = convert.Convert(av, cty.String)
|
||||
av, _ = ctyconvert.Convert(av, cty.String)
|
||||
m[name] = av.AsString()
|
||||
}
|
||||
|
||||
|
@ -104,7 +105,7 @@ func (s *GRPCProvisionerServer) ProvisionResource(req *proto.ProvisionResource_R
|
|||
cfgSchema := schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()
|
||||
cfgVal, err := msgpack.Unmarshal(req.Config.Msgpack, cfgSchema.ImpliedType())
|
||||
if err != nil {
|
||||
srvResp.Diagnostics = appendDiag(srvResp.Diagnostics, err)
|
||||
srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
|
||||
srv.Send(srvResp)
|
||||
return nil
|
||||
}
|
||||
|
@ -112,7 +113,7 @@ func (s *GRPCProvisionerServer) ProvisionResource(req *proto.ProvisionResource_R
|
|||
|
||||
connVal, err := msgpack.Unmarshal(req.Connection.Msgpack, cty.Map(cty.String))
|
||||
if err != nil {
|
||||
srvResp.Diagnostics = appendDiag(srvResp.Diagnostics, err)
|
||||
srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
|
||||
srv.Send(srvResp)
|
||||
return nil
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ func (s *GRPCProvisionerServer) ProvisionResource(req *proto.ProvisionResource_R
|
|||
|
||||
err = s.provisioner.Apply(uiOutput{srv}, instanceState, resourceConfig)
|
||||
if err != nil {
|
||||
srvResp.Diagnostics = appendDiag(srvResp.Diagnostics, err)
|
||||
srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err)
|
||||
srv.Send(srvResp)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
)
|
||||
|
||||
// protoSchemaBlock takes a *configschema.Block and converts it to a
|
||||
// proto.Schema_Block for a grpc response.
|
||||
func protoSchemaBlock(b *configschema.Block) *proto.Schema_Block {
|
||||
block := &proto.Schema_Block{}
|
||||
|
||||
for _, name := range sortedKeys(b.Attributes) {
|
||||
a := b.Attributes[name]
|
||||
attr := &proto.Schema_Attribute{
|
||||
Name: name,
|
||||
Description: a.Description,
|
||||
Optional: a.Optional,
|
||||
Computed: a.Computed,
|
||||
Required: a.Required,
|
||||
Sensitive: a.Sensitive,
|
||||
}
|
||||
|
||||
ty, err := json.Marshal(a.Type)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
attr.Type = ty
|
||||
|
||||
block.Attributes = append(block.Attributes, attr)
|
||||
}
|
||||
|
||||
for _, name := range sortedKeys(b.BlockTypes) {
|
||||
b := b.BlockTypes[name]
|
||||
block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(name, b))
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock {
|
||||
return &proto.Schema_NestedBlock{
|
||||
TypeName: name,
|
||||
Block: protoSchemaBlock(&b.Block),
|
||||
Nesting: proto.Schema_NestedBlock_NestingMode(b.Nesting),
|
||||
MinItems: int64(b.MinItems),
|
||||
MaxItems: int64(b.MaxItems),
|
||||
}
|
||||
}
|
||||
|
||||
// sortedKeys returns the lexically sorted keys from the given map. This is
|
||||
// used to make schema conversions are deterministic. This panics if map keys
|
||||
// are not a string.
|
||||
func sortedKeys(m interface{}) []string {
|
||||
v := reflect.ValueOf(m)
|
||||
keys := make([]string, v.Len())
|
||||
|
||||
mapKeys := v.MapKeys()
|
||||
for i, k := range mapKeys {
|
||||
keys[i] = k.Interface().(string)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
|
@ -1,516 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
)
|
||||
|
||||
// the TestProvider functions have been adapted from the helper/schema fixtures
|
||||
|
||||
func TestProviderGetSchema(t *testing.T) {
|
||||
p := &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"bar": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
SchemaVersion: 1,
|
||||
Schema: map[string]*schema.Schema{
|
||||
"bar": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"baz": &schema.Resource{
|
||||
SchemaVersion: 2,
|
||||
Schema: map[string]*schema.Schema{
|
||||
"bur": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
want := providers.GetSchemaResponse{
|
||||
Provider: providers.Schema{
|
||||
Version: 0,
|
||||
Block: schema.InternalMap(p.Schema).CoreConfigSchema(),
|
||||
},
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"foo": {
|
||||
Version: 1,
|
||||
Block: p.ResourcesMap["foo"].CoreConfigSchema(),
|
||||
},
|
||||
},
|
||||
DataSources: map[string]providers.Schema{
|
||||
"baz": {
|
||||
Version: 2,
|
||||
Block: p.DataSourcesMap["baz"].CoreConfigSchema(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
provider := &GRPCProviderServer{
|
||||
provider: p,
|
||||
}
|
||||
|
||||
resp, err := provider.GetSchema(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
schemaResp := providers.GetSchemaResponse{
|
||||
Provider: plugin.ProtoToProviderSchema(resp.Provider),
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"foo": plugin.ProtoToProviderSchema(resp.ResourceSchemas["foo"]),
|
||||
},
|
||||
DataSources: map[string]providers.Schema{
|
||||
"baz": plugin.ProtoToProviderSchema(resp.DataSourceSchemas["baz"]),
|
||||
},
|
||||
}
|
||||
|
||||
if !cmp.Equal(schemaResp, want, equateEmpty, typeComparer) {
|
||||
t.Error("wrong result:\n", cmp.Diff(schemaResp, want, equateEmpty, typeComparer))
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
P *schema.Provider
|
||||
Err bool
|
||||
Warn bool
|
||||
}{
|
||||
{
|
||||
Name: "warning",
|
||||
P: &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: func(_ interface{}, _ string) ([]string, []error) {
|
||||
return []string{"warning"}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Warn: true,
|
||||
},
|
||||
{
|
||||
Name: "error",
|
||||
P: &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: func(_ interface{}, _ string) ([]string, []error) {
|
||||
return nil, []error{errors.New("error")}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
provider := &GRPCProviderServer{
|
||||
provider: tc.P,
|
||||
}
|
||||
|
||||
cfgSchema := schema.InternalMap(tc.P.Schema).CoreConfigSchema()
|
||||
val := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"foo": "bar"})
|
||||
val, err := cfgSchema.CoerceValue(val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := msgpack.Marshal(val, cfgSchema.ImpliedType())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := &proto.ValidateProviderConfig_Request{
|
||||
Config: &proto.DynamicValue{Msgpack: mp},
|
||||
}
|
||||
|
||||
resp, err := provider.ValidateProviderConfig(nil, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
|
||||
var warn tfdiags.Diagnostic
|
||||
for _, d := range diags {
|
||||
if d.Severity() == tfdiags.Warning {
|
||||
warn = d
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case tc.Err:
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
case !tc.Err:
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
case tc.Warn:
|
||||
if warn == nil {
|
||||
t.Fatal("expected warning")
|
||||
}
|
||||
case !tc.Warn:
|
||||
if warn != nil {
|
||||
t.Fatal("unexpected warning", warn)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderValidateResource(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
P *schema.Provider
|
||||
Type string
|
||||
Config map[string]interface{}
|
||||
Err bool
|
||||
Warn bool
|
||||
}{
|
||||
{
|
||||
Name: "error",
|
||||
P: &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"attr": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: func(_ interface{}, _ string) ([]string, []error) {
|
||||
return nil, []error{errors.New("warn")}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: "foo",
|
||||
Err: true,
|
||||
},
|
||||
{
|
||||
Name: "ok",
|
||||
P: &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"attr": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Config: map[string]interface{}{"attr": "bar"},
|
||||
Type: "foo",
|
||||
},
|
||||
{
|
||||
Name: "warn",
|
||||
P: &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"attr": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: func(_ interface{}, _ string) ([]string, []error) {
|
||||
return []string{"warn"}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: "foo",
|
||||
Config: map[string]interface{}{"attr": "bar"},
|
||||
Err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
provider := &GRPCProviderServer{
|
||||
provider: tc.P,
|
||||
}
|
||||
|
||||
cfgSchema := tc.P.ResourcesMap[tc.Type].CoreConfigSchema()
|
||||
val := hcl2shim.HCL2ValueFromConfigValue(tc.Config)
|
||||
val, err := cfgSchema.CoerceValue(val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := msgpack.Marshal(val, cfgSchema.ImpliedType())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := &proto.ValidateResourceTypeConfig_Request{
|
||||
TypeName: tc.Type,
|
||||
Config: &proto.DynamicValue{Msgpack: mp},
|
||||
}
|
||||
|
||||
resp, err := provider.ValidateResourceTypeConfig(nil, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
|
||||
var warn tfdiags.Diagnostic
|
||||
for _, d := range diags {
|
||||
if d.Severity() == tfdiags.Warning {
|
||||
warn = d
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case tc.Err:
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
case !tc.Err:
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
case tc.Warn:
|
||||
if warn == nil {
|
||||
t.Fatal("expected warning")
|
||||
}
|
||||
case !tc.Warn:
|
||||
if warn != nil {
|
||||
t.Fatal("unexpected warning", warn)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderImportState_default(t *testing.T) {
|
||||
|
||||
p := &GRPCProviderServer{
|
||||
provider: &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
Importer: &schema.ResourceImporter{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
req := &proto.ImportResourceState_Request{
|
||||
TypeName: "foo",
|
||||
Id: "bar",
|
||||
}
|
||||
resp, err := p.ImportResourceState(nil, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
if len(resp.ImportedResources) != 1 {
|
||||
t.Fatalf("expected 1 import, git %#v", resp.ImportedResources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderImportState_setsId(t *testing.T) {
|
||||
var val string
|
||||
stateFunc := func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
val = d.Id()
|
||||
return []*schema.ResourceData{d}, nil
|
||||
}
|
||||
|
||||
p := &GRPCProviderServer{
|
||||
provider: &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: stateFunc,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
req := &proto.ImportResourceState_Request{
|
||||
TypeName: "foo",
|
||||
Id: "bar",
|
||||
}
|
||||
resp, err := p.ImportResourceState(nil, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
if len(resp.ImportedResources) != 1 {
|
||||
t.Fatalf("expected 1 import, git %#v", resp.ImportedResources)
|
||||
}
|
||||
|
||||
if val != "bar" {
|
||||
t.Fatal("should set id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderImportState_setsType(t *testing.T) {
|
||||
var tVal string
|
||||
stateFunc := func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
d.SetId("foo")
|
||||
tVal = d.State().Ephemeral.Type
|
||||
return []*schema.ResourceData{d}, nil
|
||||
}
|
||||
|
||||
p := &GRPCProviderServer{
|
||||
provider: &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"foo": &schema.Resource{
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: stateFunc,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
req := &proto.ImportResourceState_Request{
|
||||
TypeName: "foo",
|
||||
Id: "bar",
|
||||
}
|
||||
resp, err := p.ImportResourceState(nil, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
if tVal != "foo" {
|
||||
t.Fatal("should set type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderStop(t *testing.T) {
|
||||
var p schema.Provider
|
||||
|
||||
if p.Stopped() {
|
||||
t.Fatal("should not be stopped")
|
||||
}
|
||||
|
||||
// Verify stopch blocks
|
||||
ch := p.StopContext().Done()
|
||||
select {
|
||||
case <-ch:
|
||||
t.Fatal("should not be stopped")
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
|
||||
provider := &GRPCProviderServer{
|
||||
provider: &p,
|
||||
}
|
||||
|
||||
// Stop it
|
||||
if _, err := provider.Stop(nil, &proto.Stop_Request{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Verify
|
||||
if !p.Stopped() {
|
||||
t.Fatal("should be stopped")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
t.Fatal("should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderStop_stopFirst(t *testing.T) {
|
||||
var p schema.Provider
|
||||
|
||||
provider := &GRPCProviderServer{
|
||||
provider: &p,
|
||||
}
|
||||
|
||||
// Stop it
|
||||
_, err := provider.Stop(nil, &proto.Stop_Request{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Verify
|
||||
if !p.Stopped() {
|
||||
t.Fatal("should be stopped")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-p.StopContext().Done():
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
t.Fatal("should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
// add the implicit "id" attribute for test resources
|
||||
func testResource(block *configschema.Block) *configschema.Block {
|
||||
if block.Attributes == nil {
|
||||
block.Attributes = make(map[string]*configschema.Attribute)
|
||||
}
|
||||
|
||||
if block.BlockTypes == nil {
|
||||
block.BlockTypes = make(map[string]*configschema.NestedBlock)
|
||||
}
|
||||
|
||||
if block.Attributes["id"] == nil {
|
||||
block.Attributes["id"] = &configschema.Attribute{
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
}
|
||||
}
|
||||
return block
|
||||
}
|
|
@ -1,338 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/msgpack"
|
||||
|
||||
mockproto "github.com/hashicorp/terraform/plugin/mock_proto"
|
||||
)
|
||||
|
||||
// TestProvisioner functions in this file have been adapted from the
|
||||
// helper/schema tests.
|
||||
|
||||
func noopApply(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestProvisionerValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
P *schema.Provisioner
|
||||
Config map[string]interface{}
|
||||
Err bool
|
||||
Warns []string
|
||||
}{
|
||||
{
|
||||
Name: "No ApplyFunc",
|
||||
P: &schema.Provisioner{},
|
||||
Config: map[string]interface{}{},
|
||||
Err: true,
|
||||
},
|
||||
{
|
||||
"Basic required field set",
|
||||
&schema.Provisioner{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Required: true,
|
||||
Type: schema.TypeString,
|
||||
},
|
||||
},
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
false,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
Name: "Warning from property validation",
|
||||
P: &schema.Provisioner{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
ws = append(ws, "Simple warning from property validation")
|
||||
return
|
||||
},
|
||||
},
|
||||
},
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
Config: map[string]interface{}{
|
||||
"foo": "",
|
||||
},
|
||||
Err: false,
|
||||
Warns: []string{"Simple warning from property validation"},
|
||||
},
|
||||
{
|
||||
Name: "No schema",
|
||||
P: &schema.Provisioner{
|
||||
Schema: nil,
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
Config: map[string]interface{}{},
|
||||
Err: false,
|
||||
},
|
||||
{
|
||||
Name: "Warning from provisioner ValidateFunc",
|
||||
P: &schema.Provisioner{
|
||||
Schema: nil,
|
||||
ApplyFunc: noopApply,
|
||||
ValidateFunc: func(*terraform.ResourceConfig) (ws []string, errors []error) {
|
||||
ws = append(ws, "Simple warning from provisioner ValidateFunc")
|
||||
return
|
||||
},
|
||||
},
|
||||
Config: map[string]interface{}{},
|
||||
Err: false,
|
||||
Warns: []string{"Simple warning from provisioner ValidateFunc"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||
p := &GRPCProvisionerServer{
|
||||
provisioner: tc.P,
|
||||
}
|
||||
|
||||
cfgSchema := schema.InternalMap(tc.P.Schema).CoreConfigSchema()
|
||||
val := hcl2shim.HCL2ValueFromConfigValue(tc.Config)
|
||||
|
||||
val, err := cfgSchema.CoerceValue(val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err := msgpack.Marshal(val, cfgSchema.ImpliedType())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := &proto.ValidateProvisionerConfig_Request{
|
||||
Config: &proto.DynamicValue{Msgpack: mp},
|
||||
}
|
||||
|
||||
resp, err := p.ValidateProvisionerConfig(nil, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
diags := plugin.ProtoToDiagnostics(resp.Diagnostics)
|
||||
|
||||
if diags.HasErrors() != tc.Err {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
var ws []string
|
||||
for _, d := range diags {
|
||||
if d.Severity() == tfdiags.Warning {
|
||||
ws = append(ws, d.Description().Summary)
|
||||
}
|
||||
}
|
||||
|
||||
if (tc.Warns != nil || len(ws) != 0) && !reflect.DeepEqual(ws, tc.Warns) {
|
||||
t.Fatalf("%d: warnings mismatch, actual: %#v", i, ws)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerApply(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
P *schema.Provisioner
|
||||
Conn map[string]interface{}
|
||||
Config map[string]interface{}
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
Name: "Basic config",
|
||||
P: &schema.Provisioner{
|
||||
ConnSchema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
ApplyFunc: func(ctx context.Context) error {
|
||||
cd := ctx.Value(schema.ProvConnDataKey).(*schema.ResourceData)
|
||||
d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData)
|
||||
if d.Get("foo").(int) != 42 {
|
||||
return fmt.Errorf("bad config data")
|
||||
}
|
||||
if cd.Get("foo").(string) != "bar" {
|
||||
return fmt.Errorf("bad conn data")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
Conn: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
Config: map[string]interface{}{
|
||||
"foo": 42,
|
||||
},
|
||||
Err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||
p := &GRPCProvisionerServer{
|
||||
provisioner: tc.P,
|
||||
}
|
||||
|
||||
cfgSchema := schema.InternalMap(tc.P.Schema).CoreConfigSchema()
|
||||
val := hcl2shim.HCL2ValueFromConfigValue(tc.Config)
|
||||
|
||||
val, err := cfgSchema.CoerceValue(val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfgMP, err := msgpack.Marshal(val, cfgSchema.ImpliedType())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
connVal := hcl2shim.HCL2ValueFromConfigValue(tc.Conn)
|
||||
|
||||
connMP, err := msgpack.Marshal(connVal, cty.Map(cty.String))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := &proto.ProvisionResource_Request{
|
||||
Config: &proto.DynamicValue{Msgpack: cfgMP},
|
||||
Connection: &proto.DynamicValue{Msgpack: connMP},
|
||||
}
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
srv := mockproto.NewMockProvisioner_ProvisionResourceServer(ctrl)
|
||||
srv.EXPECT().Send(gomock.Any()).Return(nil)
|
||||
|
||||
err = p.ProvisionResource(req, srv)
|
||||
if err != nil && !tc.Err {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerStop(t *testing.T) {
|
||||
p := &GRPCProvisionerServer{
|
||||
provisioner: &schema.Provisioner{},
|
||||
}
|
||||
|
||||
// Verify stopch blocks
|
||||
ch := p.provisioner.StopContext().Done()
|
||||
select {
|
||||
case <-ch:
|
||||
t.Fatal("should not be stopped")
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
|
||||
// Stop it
|
||||
resp, err := p.Stop(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resp.Error != "" {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
t.Fatal("should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerStop_apply(t *testing.T) {
|
||||
p := &schema.Provisioner{
|
||||
ConnSchema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"foo": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
ApplyFunc: func(ctx context.Context) error {
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
s := &GRPCProvisionerServer{
|
||||
provisioner: p,
|
||||
}
|
||||
srv := mockproto.NewMockProvisioner_ProvisionResourceServer(gomock.NewController(t))
|
||||
srv.EXPECT().Send(gomock.Any()).Return(nil)
|
||||
|
||||
// Run the apply in a goroutine
|
||||
doneCh := make(chan struct{})
|
||||
go func() {
|
||||
req := &proto.ProvisionResource_Request{
|
||||
Config: &proto.DynamicValue{Msgpack: []byte("\201\243foo*")},
|
||||
Connection: &proto.DynamicValue{Msgpack: []byte("\201\243foo\243bar")},
|
||||
}
|
||||
err := s.ProvisionResource(req, srv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
close(doneCh)
|
||||
}()
|
||||
|
||||
// Should block
|
||||
select {
|
||||
case <-doneCh:
|
||||
t.Fatal("should not be done")
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
|
||||
resp, err := s.Stop(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Error != "" {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-doneCh:
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
t.Fatal("should be done")
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Test that we can convert configschema to protobuf types and back again.
|
||||
func TestConvertSchemaBlocks(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Want *proto.Schema_Block
|
||||
Block *configschema.Block
|
||||
}{
|
||||
"attributes": {
|
||||
&proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "computed",
|
||||
Type: []byte(`["list","bool"]`),
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "optional",
|
||||
Type: []byte(`"string"`),
|
||||
Optional: true,
|
||||
},
|
||||
{
|
||||
Name: "optional_computed",
|
||||
Type: []byte(`["map","bool"]`),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "required",
|
||||
Type: []byte(`"number"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"computed": {
|
||||
Type: cty.List(cty.Bool),
|
||||
Computed: true,
|
||||
},
|
||||
"optional": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"optional_computed": {
|
||||
Type: cty.Map(cty.Bool),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"required": {
|
||||
Type: cty.Number,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blocks": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "map",
|
||||
Nesting: proto.Schema_NestedBlock_MAP,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: []byte(`"dynamic"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
},
|
||||
"map": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingMap,
|
||||
},
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"foo": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"deep block nesting": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := protoSchemaBlock(tc.Block)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema.
|
||||
func ProtoToProviderSchema(s *proto.Schema) providers.Schema {
|
||||
return providers.Schema{
|
||||
Version: uint64(s.Version),
|
||||
Block: schemaBlock(s.Block),
|
||||
}
|
||||
}
|
||||
|
||||
// schemaBlock takes the GetSchcema_Block from a grpc response and converts it
|
||||
// to a terraform *configschema.Block.
|
||||
func schemaBlock(b *proto.Schema_Block) *configschema.Block {
|
||||
block := &configschema.Block{
|
||||
Attributes: make(map[string]*configschema.Attribute),
|
||||
BlockTypes: make(map[string]*configschema.NestedBlock),
|
||||
}
|
||||
|
||||
for _, a := range b.Attributes {
|
||||
attr := &configschema.Attribute{
|
||||
Description: a.Description,
|
||||
Required: a.Required,
|
||||
Optional: a.Optional,
|
||||
Computed: a.Computed,
|
||||
Sensitive: a.Sensitive,
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block.Attributes[a.Name] = attr
|
||||
}
|
||||
|
||||
for _, b := range b.BlockTypes {
|
||||
block.BlockTypes[b.TypeName] = schemaNestedBlock(b)
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock {
|
||||
nb := &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingMode(b.Nesting),
|
||||
MinItems: int(b.MinItems),
|
||||
MaxItems: int(b.MaxItems),
|
||||
}
|
||||
|
||||
nested := schemaBlock(b.Block)
|
||||
nb.Block = *nested
|
||||
return nb
|
||||
}
|
||||
|
||||
// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics.
|
||||
func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
for _, d := range ds {
|
||||
var severity tfdiags.Severity
|
||||
|
||||
switch d.Severity {
|
||||
case proto.Diagnostic_ERROR:
|
||||
severity = tfdiags.Error
|
||||
case proto.Diagnostic_WARNING:
|
||||
severity = tfdiags.Warning
|
||||
}
|
||||
|
||||
var newDiag tfdiags.Diagnostic
|
||||
|
||||
// if there's an attribute path, we need to create a AttributeValue diagnostic
|
||||
if d.Attribute != nil {
|
||||
path := attributePath(d.Attribute)
|
||||
newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path)
|
||||
} else {
|
||||
newDiag = tfdiags.Sourceless(severity, d.Summary, d.Detail)
|
||||
}
|
||||
|
||||
diags = diags.Append(newDiag)
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
// attributePath takes the proto encoded path and converts it to a cty.Path
|
||||
func attributePath(ap *proto.AttributePath) cty.Path {
|
||||
var p cty.Path
|
||||
for _, step := range ap.Steps {
|
||||
switch selector := step.Selector.(type) {
|
||||
case *proto.AttributePath_Step_AttributeName:
|
||||
p = p.GetAttr(selector.AttributeName)
|
||||
case *proto.AttributePath_Step_ElementKeyString:
|
||||
p = p.Index(cty.StringVal(selector.ElementKeyString))
|
||||
case *proto.AttributePath_Step_ElementKeyInt:
|
||||
p = p.Index(cty.NumberIntVal(selector.ElementKeyInt))
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// WarnsAndErrorsToProto converts the warnings and errors return by the legacy
|
||||
// provider to protobuf diagnostics.
|
||||
func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) {
|
||||
for _, w := range warns {
|
||||
diags = AppendProtoDiag(diags, w)
|
||||
}
|
||||
|
||||
for _, e := range errs {
|
||||
diags = AppendProtoDiag(diags, e)
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
// AppendProtoDiag appends a new diagnostic from a warning string or an error.
|
||||
// This panics if d is not a string or error.
|
||||
func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic {
|
||||
switch d := d.(type) {
|
||||
case error:
|
||||
diags = append(diags, &proto.Diagnostic{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: d.Error(),
|
||||
})
|
||||
case string:
|
||||
diags = append(diags, &proto.Diagnostic{
|
||||
Severity: proto.Diagnostic_WARNING,
|
||||
Summary: d,
|
||||
})
|
||||
case *proto.Diagnostic:
|
||||
diags = append(diags, d)
|
||||
case []*proto.Diagnostic:
|
||||
diags = append(diags, d...)
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics.
|
||||
func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
for _, d := range ds {
|
||||
var severity tfdiags.Severity
|
||||
|
||||
switch d.Severity {
|
||||
case proto.Diagnostic_ERROR:
|
||||
severity = tfdiags.Error
|
||||
case proto.Diagnostic_WARNING:
|
||||
severity = tfdiags.Warning
|
||||
}
|
||||
|
||||
var newDiag tfdiags.Diagnostic
|
||||
|
||||
// if there's an attribute path, we need to create a AttributeValue diagnostic
|
||||
if d.Attribute != nil {
|
||||
path := AttributePathToPath(d.Attribute)
|
||||
newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path)
|
||||
} else {
|
||||
newDiag = tfdiags.Sourceless(severity, d.Summary, d.Detail)
|
||||
}
|
||||
|
||||
diags = diags.Append(newDiag)
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
// AttributePathToPath takes the proto encoded path and converts it to a cty.Path
|
||||
func AttributePathToPath(ap *proto.AttributePath) cty.Path {
|
||||
var p cty.Path
|
||||
for _, step := range ap.Steps {
|
||||
switch selector := step.Selector.(type) {
|
||||
case *proto.AttributePath_Step_AttributeName:
|
||||
p = p.GetAttr(selector.AttributeName)
|
||||
case *proto.AttributePath_Step_ElementKeyString:
|
||||
p = p.Index(cty.StringVal(selector.ElementKeyString))
|
||||
case *proto.AttributePath_Step_ElementKeyInt:
|
||||
p = p.Index(cty.NumberIntVal(selector.ElementKeyInt))
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
|
@ -1,191 +1,48 @@
|
|||
package plugin
|
||||
package convert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
var (
|
||||
equateEmpty = cmpopts.EquateEmpty()
|
||||
typeComparer = cmp.Comparer(cty.Type.Equals)
|
||||
valueComparer = cmp.Comparer(cty.Value.RawEquals)
|
||||
)
|
||||
func TestProtoDiagnostics(t *testing.T) {
|
||||
diags := WarnsAndErrsToProto(
|
||||
[]string{
|
||||
"warning 1",
|
||||
"warning 2",
|
||||
},
|
||||
[]error{
|
||||
errors.New("error 1"),
|
||||
errors.New("error 2"),
|
||||
},
|
||||
)
|
||||
|
||||
// Test that we can convert configschema to protobuf types and back again.
|
||||
func TestConvertSchemaBlocks(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Block *proto.Schema_Block
|
||||
Want *configschema.Block
|
||||
}{
|
||||
"attributes": {
|
||||
&proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "computed",
|
||||
Type: []byte(`["list","bool"]`),
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "optional",
|
||||
Type: []byte(`"string"`),
|
||||
Optional: true,
|
||||
},
|
||||
{
|
||||
Name: "optional_computed",
|
||||
Type: []byte(`["map","bool"]`),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "required",
|
||||
Type: []byte(`"number"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"computed": {
|
||||
Type: cty.List(cty.Bool),
|
||||
Computed: true,
|
||||
},
|
||||
"optional": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"optional_computed": {
|
||||
Type: cty.Map(cty.Bool),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"required": {
|
||||
Type: cty.Number,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected := []*proto.Diagnostic{
|
||||
{
|
||||
Severity: proto.Diagnostic_WARNING,
|
||||
Summary: "warning 1",
|
||||
},
|
||||
"blocks": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "map",
|
||||
Nesting: proto.Schema_NestedBlock_MAP,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: []byte(`"dynamic"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
},
|
||||
"map": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingMap,
|
||||
},
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"foo": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Severity: proto.Diagnostic_WARNING,
|
||||
Summary: "warning 2",
|
||||
},
|
||||
"deep block nesting": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: "error 1",
|
||||
},
|
||||
{
|
||||
Severity: proto.Diagnostic_ERROR,
|
||||
Summary: "error 2",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := schemaBlock(tc.Block)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty))
|
||||
}
|
||||
})
|
||||
if !cmp.Equal(expected, diags) {
|
||||
t.Fatal(cmp.Diff(expected, diags))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
)
|
||||
|
||||
// ConfigSchemaToProto takes a *configschema.Block and converts it to a
|
||||
// proto.Schema_Block for a grpc response.
|
||||
func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block {
|
||||
block := &proto.Schema_Block{}
|
||||
|
||||
for _, name := range sortedKeys(b.Attributes) {
|
||||
a := b.Attributes[name]
|
||||
attr := &proto.Schema_Attribute{
|
||||
Name: name,
|
||||
Description: a.Description,
|
||||
Optional: a.Optional,
|
||||
Computed: a.Computed,
|
||||
Required: a.Required,
|
||||
Sensitive: a.Sensitive,
|
||||
}
|
||||
|
||||
ty, err := json.Marshal(a.Type)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
attr.Type = ty
|
||||
|
||||
block.Attributes = append(block.Attributes, attr)
|
||||
}
|
||||
|
||||
for _, name := range sortedKeys(b.BlockTypes) {
|
||||
b := b.BlockTypes[name]
|
||||
block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(name, b))
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock {
|
||||
return &proto.Schema_NestedBlock{
|
||||
TypeName: name,
|
||||
Block: ConfigSchemaToProto(&b.Block),
|
||||
Nesting: proto.Schema_NestedBlock_NestingMode(b.Nesting),
|
||||
MinItems: int64(b.MinItems),
|
||||
MaxItems: int64(b.MaxItems),
|
||||
}
|
||||
}
|
||||
|
||||
// ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema.
|
||||
func ProtoToProviderSchema(s *proto.Schema) providers.Schema {
|
||||
return providers.Schema{
|
||||
Version: uint64(s.Version),
|
||||
Block: ProtoToConfigSchema(s.Block),
|
||||
}
|
||||
}
|
||||
|
||||
// ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it
|
||||
// to a terraform *configschema.Block.
|
||||
func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block {
|
||||
block := &configschema.Block{
|
||||
Attributes: make(map[string]*configschema.Attribute),
|
||||
BlockTypes: make(map[string]*configschema.NestedBlock),
|
||||
}
|
||||
|
||||
for _, a := range b.Attributes {
|
||||
attr := &configschema.Attribute{
|
||||
Description: a.Description,
|
||||
Required: a.Required,
|
||||
Optional: a.Optional,
|
||||
Computed: a.Computed,
|
||||
Sensitive: a.Sensitive,
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
block.Attributes[a.Name] = attr
|
||||
}
|
||||
|
||||
for _, b := range b.BlockTypes {
|
||||
block.BlockTypes[b.TypeName] = schemaNestedBlock(b)
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock {
|
||||
nb := &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingMode(b.Nesting),
|
||||
MinItems: int(b.MinItems),
|
||||
MaxItems: int(b.MaxItems),
|
||||
}
|
||||
|
||||
nested := ProtoToConfigSchema(b.Block)
|
||||
nb.Block = *nested
|
||||
return nb
|
||||
}
|
||||
|
||||
// sortedKeys returns the lexically sorted keys from the given map. This is
|
||||
// used to make schema conversions are deterministic. This panics if map keys
|
||||
// are not a string.
|
||||
func sortedKeys(m interface{}) []string {
|
||||
v := reflect.ValueOf(m)
|
||||
keys := make([]string, v.Len())
|
||||
|
||||
mapKeys := v.MapKeys()
|
||||
for i, k := range mapKeys {
|
||||
keys[i] = k.Interface().(string)
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
|
@ -0,0 +1,361 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
var (
|
||||
equateEmpty = cmpopts.EquateEmpty()
|
||||
typeComparer = cmp.Comparer(cty.Type.Equals)
|
||||
valueComparer = cmp.Comparer(cty.Value.RawEquals)
|
||||
)
|
||||
|
||||
// Test that we can convert configschema to protobuf types and back again.
|
||||
func TestConvertSchemaBlocks(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Block *proto.Schema_Block
|
||||
Want *configschema.Block
|
||||
}{
|
||||
"attributes": {
|
||||
&proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "computed",
|
||||
Type: []byte(`["list","bool"]`),
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "optional",
|
||||
Type: []byte(`"string"`),
|
||||
Optional: true,
|
||||
},
|
||||
{
|
||||
Name: "optional_computed",
|
||||
Type: []byte(`["map","bool"]`),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "required",
|
||||
Type: []byte(`"number"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"computed": {
|
||||
Type: cty.List(cty.Bool),
|
||||
Computed: true,
|
||||
},
|
||||
"optional": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"optional_computed": {
|
||||
Type: cty.Map(cty.Bool),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"required": {
|
||||
Type: cty.Number,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blocks": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "map",
|
||||
Nesting: proto.Schema_NestedBlock_MAP,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: []byte(`"dynamic"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
},
|
||||
"map": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingMap,
|
||||
},
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"foo": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"deep block nesting": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := ProtoToConfigSchema(tc.Block)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we can convert configschema to protobuf types and back again.
|
||||
func TestConvertProtoSchemaBlocks(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Want *proto.Schema_Block
|
||||
Block *configschema.Block
|
||||
}{
|
||||
"attributes": {
|
||||
&proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "computed",
|
||||
Type: []byte(`["list","bool"]`),
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "optional",
|
||||
Type: []byte(`"string"`),
|
||||
Optional: true,
|
||||
},
|
||||
{
|
||||
Name: "optional_computed",
|
||||
Type: []byte(`["map","bool"]`),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
{
|
||||
Name: "required",
|
||||
Type: []byte(`"number"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"computed": {
|
||||
Type: cty.List(cty.Bool),
|
||||
Computed: true,
|
||||
},
|
||||
"optional": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"optional_computed": {
|
||||
Type: cty.Map(cty.Bool),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"required": {
|
||||
Type: cty.Number,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"blocks": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "map",
|
||||
Nesting: proto.Schema_NestedBlock_MAP,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
Attributes: []*proto.Schema_Attribute{
|
||||
{
|
||||
Name: "foo",
|
||||
Type: []byte(`"dynamic"`),
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
},
|
||||
"map": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingMap,
|
||||
},
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"foo": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"deep block nesting": {
|
||||
&proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "single",
|
||||
Nesting: proto.Schema_NestedBlock_SINGLE,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "list",
|
||||
Nesting: proto.Schema_NestedBlock_LIST,
|
||||
Block: &proto.Schema_Block{
|
||||
BlockTypes: []*proto.Schema_NestedBlock{
|
||||
{
|
||||
TypeName: "set",
|
||||
Nesting: proto.Schema_NestedBlock_SET,
|
||||
Block: &proto.Schema_Block{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"single": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSingle,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"list": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingList,
|
||||
Block: configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"set": &configschema.NestedBlock{
|
||||
Nesting: configschema.NestingSet,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
converted := ConfigSchemaToProto(tc.Block)
|
||||
if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty) {
|
||||
t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"sync"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/terraform/plugin/convert"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/version"
|
||||
|
@ -116,14 +117,14 @@ func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) {
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Provider = ProtoToProviderSchema(protoResp.Provider)
|
||||
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
|
||||
|
||||
for name, res := range protoResp.ResourceSchemas {
|
||||
resp.ResourceTypes[name] = ProtoToProviderSchema(res)
|
||||
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
|
||||
}
|
||||
|
||||
for name, data := range protoResp.DataSourceSchemas {
|
||||
resp.DataSources[name] = ProtoToProviderSchema(data)
|
||||
resp.DataSources[name] = convert.ProtoToProviderSchema(data)
|
||||
}
|
||||
|
||||
p.schemas = resp
|
||||
|
@ -148,7 +149,7 @@ func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfig
|
|||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@ -172,7 +173,7 @@ func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTy
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@ -195,7 +196,7 @@ func (p *GRPCProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceCo
|
|||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@ -225,7 +226,7 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ
|
|||
|
||||
resp.UpgradedState = state
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@ -253,7 +254,7 @@ func (p *GRPCProvider) Configure(r providers.ConfigureRequest) (resp providers.C
|
|||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@ -289,7 +290,7 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
|
||||
state, err := msgpack.Unmarshal(protoResp.NewState.Msgpack, resSchema.Block.ImpliedType())
|
||||
if err != nil {
|
||||
|
@ -328,7 +329,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
|
||||
state, err := msgpack.Unmarshal(protoResp.PlannedState.Msgpack, resSchema.Block.ImpliedType())
|
||||
if err != nil {
|
||||
|
@ -339,7 +340,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
|
|||
resp.PlannedState = state
|
||||
|
||||
for _, p := range protoResp.RequiresReplace {
|
||||
resp.RequiresReplace = append(resp.RequiresReplace, attributePath(p))
|
||||
resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p))
|
||||
}
|
||||
|
||||
resp.PlannedPrivate = protoResp.PlannedPrivate
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plugin/convert"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/provisioners"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
@ -66,7 +67,7 @@ func (p *GRPCProvisioner) GetSchema() (resp provisioners.GetSchemaResponse) {
|
|||
return resp
|
||||
}
|
||||
|
||||
resp.Provisioner = schemaBlock(protoResp.Provisioner.Block)
|
||||
resp.Provisioner = convert.ProtoToConfigSchema(protoResp.Provisioner.Block)
|
||||
|
||||
p.schema = resp.Provisioner
|
||||
|
||||
|
@ -94,7 +95,7 @@ func (p *GRPCProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvi
|
|||
resp.Diagnostics = resp.Diagnostics.Append(err)
|
||||
return resp
|
||||
}
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics))
|
||||
return resp
|
||||
}
|
||||
|
||||
|
@ -142,7 +143,7 @@ func (p *GRPCProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequ
|
|||
}
|
||||
|
||||
if len(rcv.Diagnostics) > 0 {
|
||||
resp.Diagnostics = resp.Diagnostics.Append(ProtoToDiagnostics(rcv.Diagnostics))
|
||||
resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(rcv.Diagnostics))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
"github.com/hashicorp/terraform/plugin/proto"
|
||||
"github.com/hashicorp/terraform/provisioners"
|
||||
|
@ -15,6 +17,12 @@ import (
|
|||
|
||||
var _ provisioners.Interface = (*GRPCProvisioner)(nil)
|
||||
|
||||
var (
|
||||
equateEmpty = cmpopts.EquateEmpty()
|
||||
typeComparer = cmp.Comparer(cty.Type.Equals)
|
||||
valueComparer = cmp.Comparer(cty.Value.RawEquals)
|
||||
)
|
||||
|
||||
func mockProvisionerClient(t *testing.T) *mockproto.MockProvisionerClient {
|
||||
ctrl := gomock.NewController(t)
|
||||
client := mockproto.NewMockProvisionerClient(ctrl)
|
||||
|
|
Loading…
Reference in New Issue