177 lines
4.2 KiB
Go
177 lines
4.2 KiB
Go
|
package shared
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
|
||
|
pb "github.com/golang/protobuf/proto"
|
||
|
plugin "github.com/hashicorp/go-plugin"
|
||
|
"github.com/hashicorp/nomad/nomad/structs"
|
||
|
"github.com/hashicorp/nomad/plugins/base/proto"
|
||
|
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
"github.com/zclconf/go-cty/cty"
|
||
|
"github.com/zclconf/go-cty/cty/msgpack"
|
||
|
"google.golang.org/grpc"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// testSpec is an hcl Spec for testing
|
||
|
testSpec = &hclspec.Spec{
|
||
|
Block: &hclspec.Spec_Object{
|
||
|
&hclspec.Object{
|
||
|
Attributes: map[string]*hclspec.Spec{
|
||
|
"foo": &hclspec.Spec{
|
||
|
Block: &hclspec.Spec_Attr{
|
||
|
&hclspec.Attr{
|
||
|
Type: "string",
|
||
|
Required: false,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
"bar": &hclspec.Spec{
|
||
|
Block: &hclspec.Spec_Attr{
|
||
|
&hclspec.Attr{
|
||
|
Type: "number",
|
||
|
Required: true,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
"baz": &hclspec.Spec{
|
||
|
Block: &hclspec.Spec_Attr{
|
||
|
&hclspec.Attr{
|
||
|
Type: "bool",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// testConfig is used to decode a config from the testSpec
|
||
|
type testConfig struct {
|
||
|
Foo string `cty:"foo" codec:"foo"`
|
||
|
Bar int64 `cty:"bar" codec:"bar"`
|
||
|
Baz bool `cty:"baz" codec:"baz"`
|
||
|
}
|
||
|
|
||
|
func TestBasePlugin_PluginInfo_GRPC(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
require := require.New(t)
|
||
|
|
||
|
const (
|
||
|
apiVersion = "v0.1.0"
|
||
|
pluginVersion = "v0.2.1"
|
||
|
pluginName = "mock"
|
||
|
)
|
||
|
|
||
|
knownType := func() (*PluginInfoResponse, error) {
|
||
|
info := &PluginInfoResponse{
|
||
|
Type: PluginTypeBase,
|
||
|
PluginApiVersion: apiVersion,
|
||
|
PluginVersion: pluginVersion,
|
||
|
Name: pluginName,
|
||
|
}
|
||
|
return info, nil
|
||
|
}
|
||
|
unknownType := func() (*PluginInfoResponse, error) {
|
||
|
info := &PluginInfoResponse{
|
||
|
Type: "bad",
|
||
|
PluginApiVersion: apiVersion,
|
||
|
PluginVersion: pluginVersion,
|
||
|
Name: pluginName,
|
||
|
}
|
||
|
return info, nil
|
||
|
}
|
||
|
|
||
|
mock := &MockPlugin{
|
||
|
PluginInfoF: knownType,
|
||
|
}
|
||
|
|
||
|
conn, server := plugin.TestGRPCConn(t, func(s *grpc.Server) {
|
||
|
proto.RegisterBasePluginServer(s, &basePluginServer{impl: mock})
|
||
|
})
|
||
|
defer conn.Close()
|
||
|
defer server.Stop()
|
||
|
grpcClient := proto.NewBasePluginClient(conn)
|
||
|
client := basePluginClient{client: grpcClient}
|
||
|
|
||
|
resp, err := client.PluginInfo()
|
||
|
require.NoError(err)
|
||
|
require.Equal(apiVersion, resp.PluginApiVersion)
|
||
|
require.Equal(pluginVersion, resp.PluginVersion)
|
||
|
require.Equal(pluginName, resp.Name)
|
||
|
require.Equal(PluginTypeBase, resp.Type)
|
||
|
|
||
|
// Swap the implementation to return an unknown type
|
||
|
mock.PluginInfoF = unknownType
|
||
|
_, err = client.PluginInfo()
|
||
|
require.Error(err)
|
||
|
require.Contains(err.Error(), "unknown type")
|
||
|
}
|
||
|
|
||
|
func TestBasePlugin_ConfigSchema(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
require := require.New(t)
|
||
|
|
||
|
mock := &MockPlugin{
|
||
|
ConfigSchemaF: func() (*hclspec.Spec, error) {
|
||
|
return testSpec, nil
|
||
|
},
|
||
|
}
|
||
|
|
||
|
conn, server := plugin.TestGRPCConn(t, func(s *grpc.Server) {
|
||
|
proto.RegisterBasePluginServer(s, &basePluginServer{impl: mock})
|
||
|
})
|
||
|
defer conn.Close()
|
||
|
defer server.Stop()
|
||
|
grpcClient := proto.NewBasePluginClient(conn)
|
||
|
client := basePluginClient{client: grpcClient}
|
||
|
|
||
|
specOut, err := client.ConfigSchema()
|
||
|
require.NoError(err)
|
||
|
require.True(pb.Equal(testSpec, specOut))
|
||
|
}
|
||
|
|
||
|
func TestBasePlugin_SetConfig(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
require := require.New(t)
|
||
|
|
||
|
var receivedData []byte
|
||
|
mock := &MockPlugin{
|
||
|
ConfigSchemaF: func() (*hclspec.Spec, error) {
|
||
|
return testSpec, nil
|
||
|
},
|
||
|
SetConfigF: func(data []byte) error {
|
||
|
receivedData = data
|
||
|
return nil
|
||
|
},
|
||
|
}
|
||
|
|
||
|
conn, server := plugin.TestGRPCConn(t, func(s *grpc.Server) {
|
||
|
proto.RegisterBasePluginServer(s, &basePluginServer{impl: mock})
|
||
|
})
|
||
|
defer conn.Close()
|
||
|
defer server.Stop()
|
||
|
grpcClient := proto.NewBasePluginClient(conn)
|
||
|
client := basePluginClient{client: grpcClient}
|
||
|
|
||
|
config := cty.ObjectVal(map[string]cty.Value{
|
||
|
"foo": cty.StringVal("v1"),
|
||
|
"bar": cty.NumberIntVal(1337),
|
||
|
"baz": cty.BoolVal(true),
|
||
|
})
|
||
|
cdata, err := msgpack.Marshal(config, config.Type())
|
||
|
require.NoError(err)
|
||
|
require.NoError(client.SetConfig(cdata))
|
||
|
require.Equal(cdata, receivedData)
|
||
|
|
||
|
// Decode the value back
|
||
|
var actual testConfig
|
||
|
require.NoError(structs.Decode(receivedData, &actual))
|
||
|
require.Equal("v1", actual.Foo)
|
||
|
require.EqualValues(1337, actual.Bar)
|
||
|
require.True(actual.Baz)
|
||
|
}
|