Merge pull request #5015 from hashicorp/f-plugin-versions

Add plugin API versioning to plugin loader and plugins
This commit is contained in:
Alex Dadgar
2018-12-18 16:49:02 -08:00
committed by GitHub
43 changed files with 1075 additions and 419 deletions

View File

@@ -9,7 +9,6 @@ import (
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/nomad/structs/config"
@@ -368,8 +367,8 @@ func (c *Config) ReadStringListToMapDefault(key, defaultValue string) map[string
}
// NomadPluginConfig produces the NomadConfig struct which is sent to Nomad plugins
func (c *Config) NomadPluginConfig() *base.ClientAgentConfig {
return &base.ClientAgentConfig{
func (c *Config) NomadPluginConfig() *base.AgentConfig {
return &base.AgentConfig{
Driver: &base.ClientDriverConfig{
ClientMinPort: c.ClientMinPort,
ClientMaxPort: c.ClientMaxPort,

View File

@@ -40,7 +40,7 @@ type instanceManagerConfig struct {
StoreReattach StorePluginReattachFn
// PluginConfig is the config passed to the launched plugins
PluginConfig *base.ClientAgentConfig
PluginConfig *base.AgentConfig
// Id is the ID of the plugin being managed
Id *loader.PluginID
@@ -70,7 +70,7 @@ type instanceManager struct {
storeReattach StorePluginReattachFn
// pluginConfig is the config passed to the launched plugins
pluginConfig *base.ClientAgentConfig
pluginConfig *base.AgentConfig
// id is the ID of the plugin being managed
id *loader.PluginID

View File

@@ -63,7 +63,7 @@ type Config struct {
Loader loader.PluginCatalog
// PluginConfig is the config passed to the launched plugins
PluginConfig *base.ClientAgentConfig
PluginConfig *base.AgentConfig
// Updater is used to update the node when device information changes
Updater UpdateNodeDevicesFn
@@ -91,7 +91,7 @@ type manager struct {
loader loader.PluginCatalog
// pluginConfig is the config passed to the launched plugins
pluginConfig *base.ClientAgentConfig
pluginConfig *base.AgentConfig
// updater is used to update the node when device information changes
updater UpdateNodeDevicesFn

View File

@@ -121,7 +121,7 @@ func baseTestConfig(t *testing.T) (
// Create the config
config = &Config{
Logger: testlog.HCLogger(t),
PluginConfig: &base.ClientAgentConfig{},
PluginConfig: &base.AgentConfig{},
StatsInterval: 100 * time.Millisecond,
State: state.NewMemDB(),
Updater: updateFn,
@@ -133,7 +133,7 @@ func baseTestConfig(t *testing.T) (
func configureCatalogWith(catalog *loader.MockCatalog, plugins map[*base.PluginInfoResponse]loader.PluginInstance) {
catalog.DispenseF = func(name, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
catalog.DispenseF = func(name, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
for info, v := range plugins {
if info.Name == name {
return v, nil
@@ -167,10 +167,10 @@ func configureCatalogWith(catalog *loader.MockCatalog, plugins map[*base.PluginI
func pluginInfoResponse(name string) *base.PluginInfoResponse {
return &base.PluginInfoResponse{
Type: base.PluginTypeDevice,
PluginApiVersion: "v0.0.1",
PluginVersion: "v0.0.1",
Name: name,
Type: base.PluginTypeDevice,
PluginApiVersions: []string{"v0.0.1"},
PluginVersion: "v0.0.1",
Name: name,
}
}
@@ -209,7 +209,7 @@ func nvidiaAndIntelDefaultPlugins(catalog *loader.MockCatalog) {
ReserveF: deviceReserveFn,
StatsF: device.StaticStats([]*device.DeviceGroupStats{nvidiaDeviceGroupStats}),
}
pluginNvidia := loader.MockBasicExternalPlugin(deviceNvidia)
pluginNvidia := loader.MockBasicExternalPlugin(deviceNvidia, device.ApiVersion010)
pluginInfoIntel := pluginInfoResponse("intel")
deviceIntel := &device.MockDevicePlugin{
@@ -222,7 +222,7 @@ func nvidiaAndIntelDefaultPlugins(catalog *loader.MockCatalog) {
ReserveF: deviceReserveFn,
StatsF: device.StaticStats([]*device.DeviceGroupStats{intelDeviceGroupStats}),
}
pluginIntel := loader.MockBasicExternalPlugin(deviceIntel)
pluginIntel := loader.MockBasicExternalPlugin(deviceIntel, device.ApiVersion010)
// Configure the catalog with two plugins
configureCatalogWith(catalog, map[*base.PluginInfoResponse]loader.PluginInstance{
@@ -454,7 +454,7 @@ func TestManager_Shutdown(t *testing.T) {
m.Shutdown()
for _, resp := range catalog.Catalog()[base.PluginTypeDevice] {
pinst, _ := catalog.Dispense(resp.Name, resp.Type, &base.ClientAgentConfig{}, config.Logger)
pinst, _ := catalog.Dispense(resp.Name, resp.Type, &base.AgentConfig{}, config.Logger)
require.True(pinst.Exited())
}
}
@@ -486,7 +486,7 @@ func TestManager_Run_ShutdownOld(t *testing.T) {
testutil.WaitForResult(func() (bool, error) {
for _, resp := range catalog.Catalog()[base.PluginTypeDevice] {
pinst, _ := catalog.Dispense(resp.Name, resp.Type, &base.ClientAgentConfig{}, config.Logger)
pinst, _ := catalog.Dispense(resp.Name, resp.Type, &base.AgentConfig{}, config.Logger)
if !pinst.Exited() {
return false, fmt.Errorf("plugin %q not shutdown", resp.Name)
}

View File

@@ -565,7 +565,7 @@ func TestFingerprintManager_Run_DriverFailure(t *testing.T) {
dispenseCalls := 0
loader := &loader.MockCatalog{
DispenseF: func(name, pluginType string, cfg *base.ClientAgentConfig, logger log.Logger) (loader.PluginInstance, error) {
DispenseF: func(name, pluginType string, cfg *base.AgentConfig, logger log.Logger) (loader.PluginInstance, error) {
if pluginType == base.PluginTypeDriver && name == "raw_exec" {
dispenseCalls++
}

View File

@@ -18,10 +18,11 @@ func (a *Agent) setupPlugins() error {
// Build the plugin loader
config := &loader.PluginLoaderConfig{
Logger: a.logger,
PluginDir: a.config.PluginDir,
Configs: a.config.Plugins,
InternalPlugins: internal,
Logger: a.logger,
PluginDir: a.config.PluginDir,
Configs: a.config.Plugins,
InternalPlugins: internal,
SupportedVersions: loader.AgentSupportedApiVersions,
}
l, err := loader.NewPluginLoader(config)
if err != nil {

View File

@@ -37,10 +37,10 @@ const (
var (
// pluginInfo describes the plugin
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDevice,
PluginApiVersion: "0.0.1", // XXX This should be an array and should be consts
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDevice,
PluginApiVersions: []string{device.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the specification of the plugin's configuration
@@ -111,10 +111,12 @@ func (d *NvidiaDevice) ConfigSchema() (*hclspec.Spec, error) {
}
// SetConfig is used to set the configuration of the plugin.
func (d *NvidiaDevice) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *NvidiaDevice) SetConfig(cfg *base.Config) error {
var config Config
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(cfg.PluginConfig) != 0 {
if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
return err
}
}
for _, ignoredGPUId := range config.IgnoredGPUIDs {

View File

@@ -122,10 +122,10 @@ var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -502,10 +502,12 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *Driver) SetConfig(c *base.Config) error {
var config DriverConfig
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(c.PluginConfig) != 0 {
if err := base.MsgPackDecode(c.PluginConfig, &config); err != nil {
return err
}
}
d.config = &config
@@ -517,8 +519,8 @@ func (d *Driver) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
d.config.GC.imageDelayDuration = dur
}
if cfg != nil {
d.clientConfig = cfg.Driver
if c.AgentConfig != nil {
d.clientConfig = c.AgentConfig.Driver
}
dockerClient, _, err := d.dockerClients()

View File

@@ -169,8 +169,9 @@ func dockerDriverHarness(t *testing.T, cfg map[string]interface{}) *dtestutil.Dr
}
}
plugLoader, err := loader.NewPluginLoader(&loader.PluginLoaderConfig{
Logger: logger,
PluginDir: "./plugins",
Logger: logger,
PluginDir: "./plugins",
SupportedVersions: loader.AgentSupportedApiVersions,
InternalPlugins: map[loader.PluginID]*loader.InternalPluginConfig{
PluginID: {
Config: cfg,

View File

@@ -49,10 +49,10 @@ var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -136,9 +136,9 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(_ []byte, cfg *base.ClientAgentConfig) error {
if cfg != nil {
d.nomadConfig = cfg.Driver
func (d *Driver) SetConfig(cfg *base.Config) error {
if cfg != nil && cfg.AgentConfig != nil {
d.nomadConfig = cfg.AgentConfig.Driver
}
return nil
}

View File

@@ -54,10 +54,10 @@ var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -156,9 +156,9 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(_ []byte, cfg *base.ClientAgentConfig) error {
if cfg != nil {
d.nomadConfig = cfg.Driver
func (d *Driver) SetConfig(cfg *base.Config) error {
if cfg != nil && cfg.AgentConfig != nil {
d.nomadConfig = cfg.AgentConfig.Driver
}
return nil
}

View File

@@ -62,10 +62,10 @@ func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -200,15 +200,17 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *Driver) SetConfig(cfg *base.Config) error {
var config Config
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(cfg.PluginConfig) != 0 {
if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
return err
}
}
d.config = &config
if cfg != nil {
d.nomadConfig = cfg.Driver
if cfg.AgentConfig != nil {
d.nomadConfig = cfg.AgentConfig.Driver
}
return nil

View File

@@ -45,10 +45,10 @@ var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -225,10 +225,12 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *Driver) SetConfig(cfg *base.Config) error {
var config Config
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(cfg.PluginConfig) != 0 {
if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
return err
}
}
d.config = &config

View File

@@ -74,10 +74,10 @@ var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -167,9 +167,9 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(_ []byte, cfg *base.ClientAgentConfig) error {
if cfg != nil {
d.nomadConfig = cfg.Driver
func (d *Driver) SetConfig(cfg *base.Config) error {
if cfg.AgentConfig != nil {
d.nomadConfig = cfg.AgentConfig.Driver
}
return nil
}

View File

@@ -63,10 +63,10 @@ func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -174,15 +174,17 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *Driver) SetConfig(cfg *base.Config) error {
var config Config
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(cfg.PluginConfig) != 0 {
if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
return err
}
}
d.config = &config
if cfg != nil {
d.nomadConfig = cfg.Driver
if cfg.AgentConfig != nil {
d.nomadConfig = cfg.AgentConfig.Driver
}
return nil
}

View File

@@ -42,25 +42,30 @@ func TestRawExecDriver_SetConfig(t *testing.T) {
harness := dtestutil.NewDriverHarness(t, d)
defer harness.Kill()
bconfig := &basePlug.Config{}
// Disable raw exec.
config := &Config{}
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig.PluginConfig = data
require.NoError(harness.SetConfig(bconfig))
require.Exactly(config, d.(*Driver).config)
config.Enabled = true
config.NoCgroups = true
data = []byte{}
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig.PluginConfig = data
require.NoError(harness.SetConfig(bconfig))
require.Exactly(config, d.(*Driver).config)
config.NoCgroups = false
data = []byte{}
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig.PluginConfig = data
require.NoError(harness.SetConfig(bconfig))
require.Exactly(config, d.(*Driver).config)
}
@@ -76,7 +81,10 @@ func TestRawExecDriver_Fingerprint(t *testing.T) {
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig := &basePlug.Config{
PluginConfig: data,
}
require.NoError(harness.SetConfig(bconfig))
fingerCh, err := harness.Fingerprint(context.Background())
require.NoError(err)
@@ -168,7 +176,8 @@ func TestRawExecDriver_StartWaitStop(t *testing.T) {
config := &Config{NoCgroups: true}
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
task := &drivers.TaskConfig{
ID: uuid.Generate(),
@@ -234,7 +243,8 @@ func TestRawExecDriver_StartWaitRecoverWaitStop(t *testing.T) {
config := &Config{NoCgroups: true}
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
task := &drivers.TaskConfig{
ID: uuid.Generate(),

View File

@@ -86,10 +86,10 @@ func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
var (
// pluginInfo is the response returned for the PluginInfo RPC
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDriver,
PluginApiVersion: "0.0.1",
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDriver,
PluginApiVersions: []string{drivers.ApiVersion010},
PluginVersion: "0.1.0",
Name: pluginName,
}
// configSpec is the hcl specification returned by the ConfigSchema RPC
@@ -216,15 +216,17 @@ func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
return configSpec, nil
}
func (d *Driver) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *Driver) SetConfig(cfg *base.Config) error {
var config Config
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(cfg.PluginConfig) != 0 {
if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
return err
}
}
d.config = &config
if cfg != nil {
d.nomadConfig = cfg.Driver
if cfg.AgentConfig != nil {
d.nomadConfig = cfg.AgentConfig.Driver
}
return nil
}

View File

@@ -64,13 +64,15 @@ func TestRktDriver_SetConfig(t *testing.T) {
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
require.Exactly(config, d.(*Driver).config)
config.VolumesEnabled = false
data = []byte{}
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig = &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
require.Exactly(config, d.(*Driver).config)
}
@@ -446,7 +448,8 @@ func TestRktDriver_Start_Wait_Volume(t *testing.T) {
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
task := &drivers.TaskConfig{
ID: uuid.Generate(),
@@ -530,7 +533,8 @@ func TestRktDriver_Start_Wait_TaskMounts(t *testing.T) {
var data []byte
require.NoError(basePlug.MsgPackEncode(&data, config))
require.NoError(harness.SetConfig(data, nil))
bconfig := &basePlug.Config{PluginConfig: data}
require.NoError(harness.SetConfig(bconfig))
tmpvol, err := ioutil.TempDir("", "nomadtest_rktdriver_volumes")
require.NoError(err)

View File

@@ -15,7 +15,7 @@ type BasePlugin interface {
// SetConfig is used to set the configuration by passing a MessagePack
// encoding of it.
SetConfig(data []byte, config *ClientAgentConfig) error
SetConfig(c *Config) error
}
// PluginInfoResponse returns basic information about the plugin such that Nomad
@@ -24,9 +24,9 @@ type PluginInfoResponse struct {
// Type returns the plugins type
Type string
// PluginApiVersion returns the version of the Nomad plugin API it is built
// against.
PluginApiVersion string
// PluginApiVersions returns the versions of the Nomad plugin API that the
// plugin supports.
PluginApiVersions []string
// PluginVersion is the version of the plugin.
PluginVersion string
@@ -35,8 +35,21 @@ type PluginInfoResponse struct {
Name string
}
// ClientAgentConfig is the nomad client configuration sent to all plugins
type ClientAgentConfig struct {
// Config contains the configuration for the plugin.
type Config struct {
// ApiVersion is the negotiated plugin API version to use.
ApiVersion string
// PluginConfig is the MessagePack encoding of the plugins user
// configuration.
PluginConfig []byte
// AgentConfig is the Nomad agents configuration as applicable to plugins
AgentConfig *AgentConfig
}
// AgentConfig is the Nomad agent's configuration sent to all plugins
type AgentConfig struct {
Driver *ClientDriverConfig
}
@@ -51,7 +64,7 @@ type ClientDriverConfig struct {
ClientMinPort uint
}
func (c *ClientAgentConfig) toProto() *proto.NomadConfig {
func (c *AgentConfig) toProto() *proto.NomadConfig {
if c == nil {
return nil
}
@@ -68,12 +81,12 @@ func (c *ClientAgentConfig) toProto() *proto.NomadConfig {
return cfg
}
func nomadConfigFromProto(pb *proto.NomadConfig) *ClientAgentConfig {
func nomadConfigFromProto(pb *proto.NomadConfig) *AgentConfig {
if pb == nil {
return nil
}
cfg := &ClientAgentConfig{}
cfg := &AgentConfig{}
if pb.Driver != nil {
cfg.Driver = &ClientDriverConfig{
ClientMaxPort: uint(pb.Driver.ClientMaxPort),

View File

@@ -34,10 +34,10 @@ func (b *BasePluginClient) PluginInfo() (*PluginInfoResponse, error) {
}
resp := &PluginInfoResponse{
Type: ptype,
PluginApiVersion: presp.GetPluginApiVersion(),
PluginVersion: presp.GetPluginVersion(),
Name: presp.GetName(),
Type: ptype,
PluginApiVersions: presp.GetPluginApiVersions(),
PluginVersion: presp.GetPluginVersion(),
Name: presp.GetName(),
}
return resp, nil
@@ -52,11 +52,12 @@ func (b *BasePluginClient) ConfigSchema() (*hclspec.Spec, error) {
return presp.GetSpec(), nil
}
func (b *BasePluginClient) SetConfig(data []byte, config *ClientAgentConfig) error {
func (b *BasePluginClient) SetConfig(c *Config) error {
// Send the config
_, err := b.Client.SetConfig(b.DoneCtx, &proto.SetConfigRequest{
MsgpackConfig: data,
NomadConfig: config.toProto(),
MsgpackConfig: c.PluginConfig,
NomadConfig: c.AgentConfig.toProto(),
PluginApiVersion: c.ApiVersion,
})
return err

View File

@@ -16,27 +16,30 @@ func TestBasePlugin_PluginInfo_GRPC(t *testing.T) {
t.Parallel()
require := require.New(t)
var (
apiVersions = []string{"v0.1.0", "v0.1.1"}
)
const (
apiVersion = "v0.1.0"
pluginVersion = "v0.2.1"
pluginName = "mock"
)
knownType := func() (*PluginInfoResponse, error) {
info := &PluginInfoResponse{
Type: PluginTypeDriver,
PluginApiVersion: apiVersion,
PluginVersion: pluginVersion,
Name: pluginName,
Type: PluginTypeDriver,
PluginApiVersions: apiVersions,
PluginVersion: pluginVersion,
Name: pluginName,
}
return info, nil
}
unknownType := func() (*PluginInfoResponse, error) {
info := &PluginInfoResponse{
Type: "bad",
PluginApiVersion: apiVersion,
PluginVersion: pluginVersion,
Name: pluginName,
Type: "bad",
PluginApiVersions: apiVersions,
PluginVersion: pluginVersion,
Name: pluginName,
}
return info, nil
}
@@ -63,7 +66,7 @@ func TestBasePlugin_PluginInfo_GRPC(t *testing.T) {
resp, err := impl.PluginInfo()
require.NoError(err)
require.Equal(apiVersion, resp.PluginApiVersion)
require.Equal(apiVersions, resp.PluginApiVersions)
require.Equal(pluginVersion, resp.PluginVersion)
require.Equal(pluginName, resp.Name)
require.Equal(PluginTypeDriver, resp.Type)
@@ -118,8 +121,8 @@ func TestBasePlugin_SetConfig(t *testing.T) {
ConfigSchemaF: func() (*hclspec.Spec, error) {
return TestSpec, nil
},
SetConfigF: func(data []byte, cfg *ClientAgentConfig) error {
receivedData = data
SetConfigF: func(cfg *Config) error {
receivedData = cfg.PluginConfig
return nil
},
}
@@ -147,7 +150,7 @@ func TestBasePlugin_SetConfig(t *testing.T) {
})
cdata, err := msgpack.Marshal(config, config.Type())
require.NoError(err)
require.NoError(impl.SetConfig(cdata, &ClientAgentConfig{}))
require.NoError(impl.SetConfig(&Config{PluginConfig: cdata}))
require.Equal(cdata, receivedData)
// Decode the value back

View File

@@ -48,7 +48,7 @@ func (x PluginType) String() string {
return proto.EnumName(PluginType_name, int32(x))
}
func (PluginType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{0}
return fileDescriptor_base_f2480776612a8fbd, []int{0}
}
// PluginInfoRequest is used to request the plugins basic information.
@@ -62,7 +62,7 @@ func (m *PluginInfoRequest) Reset() { *m = PluginInfoRequest{} }
func (m *PluginInfoRequest) String() string { return proto.CompactTextString(m) }
func (*PluginInfoRequest) ProtoMessage() {}
func (*PluginInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{0}
return fileDescriptor_base_f2480776612a8fbd, []int{0}
}
func (m *PluginInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PluginInfoRequest.Unmarshal(m, b)
@@ -87,9 +87,9 @@ var xxx_messageInfo_PluginInfoRequest proto.InternalMessageInfo
type PluginInfoResponse struct {
// type indicates what type of plugin this is.
Type PluginType `protobuf:"varint,1,opt,name=type,proto3,enum=hashicorp.nomad.plugins.base.proto.PluginType" json:"type,omitempty"`
// plugin_api_version indicates the version of the Nomad Plugin API
// this plugin is built against.
PluginApiVersion string `protobuf:"bytes,2,opt,name=plugin_api_version,json=pluginApiVersion,proto3" json:"plugin_api_version,omitempty"`
// plugin_api_versions indicates the versions of the Nomad Plugin API
// this plugin supports.
PluginApiVersions []string `protobuf:"bytes,2,rep,name=plugin_api_versions,json=pluginApiVersions,proto3" json:"plugin_api_versions,omitempty"`
// plugin_version is the semver version of this individual plugin.
// This is divorce from Nomads development and versioning.
PluginVersion string `protobuf:"bytes,3,opt,name=plugin_version,json=pluginVersion,proto3" json:"plugin_version,omitempty"`
@@ -104,7 +104,7 @@ func (m *PluginInfoResponse) Reset() { *m = PluginInfoResponse{} }
func (m *PluginInfoResponse) String() string { return proto.CompactTextString(m) }
func (*PluginInfoResponse) ProtoMessage() {}
func (*PluginInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{1}
return fileDescriptor_base_f2480776612a8fbd, []int{1}
}
func (m *PluginInfoResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PluginInfoResponse.Unmarshal(m, b)
@@ -131,11 +131,11 @@ func (m *PluginInfoResponse) GetType() PluginType {
return PluginType_UNKNOWN
}
func (m *PluginInfoResponse) GetPluginApiVersion() string {
func (m *PluginInfoResponse) GetPluginApiVersions() []string {
if m != nil {
return m.PluginApiVersion
return m.PluginApiVersions
}
return ""
return nil
}
func (m *PluginInfoResponse) GetPluginVersion() string {
@@ -163,7 +163,7 @@ func (m *ConfigSchemaRequest) Reset() { *m = ConfigSchemaRequest{} }
func (m *ConfigSchemaRequest) String() string { return proto.CompactTextString(m) }
func (*ConfigSchemaRequest) ProtoMessage() {}
func (*ConfigSchemaRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{2}
return fileDescriptor_base_f2480776612a8fbd, []int{2}
}
func (m *ConfigSchemaRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConfigSchemaRequest.Unmarshal(m, b)
@@ -196,7 +196,7 @@ func (m *ConfigSchemaResponse) Reset() { *m = ConfigSchemaResponse{} }
func (m *ConfigSchemaResponse) String() string { return proto.CompactTextString(m) }
func (*ConfigSchemaResponse) ProtoMessage() {}
func (*ConfigSchemaResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{3}
return fileDescriptor_base_f2480776612a8fbd, []int{3}
}
func (m *ConfigSchemaResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConfigSchemaResponse.Unmarshal(m, b)
@@ -228,17 +228,19 @@ type SetConfigRequest struct {
// msgpack_config is the configuration encoded as MessagePack.
MsgpackConfig []byte `protobuf:"bytes,1,opt,name=msgpack_config,json=msgpackConfig,proto3" json:"msgpack_config,omitempty"`
// nomad_config is the nomad client configuration sent to all plugins.
NomadConfig *NomadConfig `protobuf:"bytes,2,opt,name=nomad_config,json=nomadConfig,proto3" json:"nomad_config,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
NomadConfig *NomadConfig `protobuf:"bytes,2,opt,name=nomad_config,json=nomadConfig,proto3" json:"nomad_config,omitempty"`
// plugin_api_version is the api version to use.
PluginApiVersion string `protobuf:"bytes,3,opt,name=plugin_api_version,json=pluginApiVersion,proto3" json:"plugin_api_version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SetConfigRequest) Reset() { *m = SetConfigRequest{} }
func (m *SetConfigRequest) String() string { return proto.CompactTextString(m) }
func (*SetConfigRequest) ProtoMessage() {}
func (*SetConfigRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{4}
return fileDescriptor_base_f2480776612a8fbd, []int{4}
}
func (m *SetConfigRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetConfigRequest.Unmarshal(m, b)
@@ -272,6 +274,13 @@ func (m *SetConfigRequest) GetNomadConfig() *NomadConfig {
return nil
}
func (m *SetConfigRequest) GetPluginApiVersion() string {
if m != nil {
return m.PluginApiVersion
}
return ""
}
// NomadConfig is the client configuration sent to all plugins
type NomadConfig struct {
// driver specific configuration sent to all plugins
@@ -285,7 +294,7 @@ func (m *NomadConfig) Reset() { *m = NomadConfig{} }
func (m *NomadConfig) String() string { return proto.CompactTextString(m) }
func (*NomadConfig) ProtoMessage() {}
func (*NomadConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{5}
return fileDescriptor_base_f2480776612a8fbd, []int{5}
}
func (m *NomadConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NomadConfig.Unmarshal(m, b)
@@ -330,7 +339,7 @@ func (m *NomadDriverConfig) Reset() { *m = NomadDriverConfig{} }
func (m *NomadDriverConfig) String() string { return proto.CompactTextString(m) }
func (*NomadDriverConfig) ProtoMessage() {}
func (*NomadDriverConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{6}
return fileDescriptor_base_f2480776612a8fbd, []int{6}
}
func (m *NomadDriverConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NomadDriverConfig.Unmarshal(m, b)
@@ -375,7 +384,7 @@ func (m *SetConfigResponse) Reset() { *m = SetConfigResponse{} }
func (m *SetConfigResponse) String() string { return proto.CompactTextString(m) }
func (*SetConfigResponse) ProtoMessage() {}
func (*SetConfigResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_base_6813a9f13eda45d4, []int{7}
return fileDescriptor_base_f2480776612a8fbd, []int{7}
}
func (m *SetConfigResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetConfigResponse.Unmarshal(m, b)
@@ -551,41 +560,42 @@ var _BasePlugin_serviceDesc = grpc.ServiceDesc{
Metadata: "plugins/base/proto/base.proto",
}
func init() { proto.RegisterFile("plugins/base/proto/base.proto", fileDescriptor_base_6813a9f13eda45d4) }
func init() { proto.RegisterFile("plugins/base/proto/base.proto", fileDescriptor_base_f2480776612a8fbd) }
var fileDescriptor_base_6813a9f13eda45d4 = []byte{
// 519 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x5d, 0x6b, 0x1a, 0x4d,
0x14, 0xc7, 0xb3, 0xea, 0x63, 0xc8, 0x51, 0x83, 0x99, 0x3c, 0x05, 0x11, 0x0a, 0x61, 0x69, 0x21,
0x94, 0xb0, 0x4b, 0x6d, 0x6d, 0x7b, 0x99, 0x6a, 0xbc, 0x90, 0x12, 0x1b, 0xd6, 0xd6, 0x96, 0x52,
0x90, 0x71, 0x9d, 0xb8, 0x43, 0x75, 0x66, 0xba, 0xb3, 0x86, 0xa6, 0xd0, 0xab, 0x5e, 0xe7, 0x43,
0xf5, 0x9b, 0x95, 0x3d, 0x33, 0xab, 0x6b, 0x5f, 0xa8, 0x5e, 0xcd, 0xf1, 0x9c, 0xdf, 0xf9, 0x9f,
0x17, 0xcf, 0xc2, 0x7d, 0x35, 0x5f, 0xce, 0xb8, 0xd0, 0xfe, 0x84, 0x6a, 0xe6, 0xab, 0x58, 0x26,
0x12, 0x4d, 0x0f, 0x4d, 0xe2, 0x46, 0x54, 0x47, 0x3c, 0x94, 0xb1, 0xf2, 0x84, 0x5c, 0xd0, 0xa9,
0x67, 0x71, 0x6f, 0xcd, 0x34, 0xcf, 0x67, 0x3c, 0x89, 0x96, 0x13, 0x2f, 0x94, 0x0b, 0x7f, 0x85,
0xfb, 0x88, 0xfb, 0x99, 0xba, 0x8e, 0x68, 0xcc, 0xa6, 0x7e, 0x14, 0xce, 0xb5, 0x62, 0x61, 0xfa,
0x8e, 0x53, 0xc3, 0x28, 0xb8, 0xc7, 0x70, 0x74, 0x85, 0x60, 0x5f, 0x5c, 0xcb, 0x80, 0x7d, 0x5e,
0x32, 0x9d, 0xb8, 0x3f, 0x1c, 0x20, 0x79, 0xaf, 0x56, 0x52, 0x68, 0x46, 0x3a, 0x50, 0x4a, 0x6e,
0x15, 0x6b, 0x38, 0x27, 0xce, 0xe9, 0x61, 0xcb, 0xf3, 0xfe, 0xdd, 0xa0, 0x67, 0x54, 0xde, 0xdc,
0x2a, 0x16, 0x60, 0x2e, 0x39, 0x03, 0x62, 0xb0, 0x31, 0x55, 0x7c, 0x7c, 0xc3, 0x62, 0xcd, 0xa5,
0x68, 0x14, 0x4e, 0x9c, 0xd3, 0x83, 0xa0, 0x6e, 0x22, 0x2f, 0x15, 0x1f, 0x19, 0x3f, 0x79, 0x08,
0x87, 0x96, 0xce, 0xc8, 0x22, 0x92, 0x35, 0xe3, 0xcd, 0x30, 0x02, 0x25, 0x41, 0x17, 0xac, 0x51,
0xc2, 0x20, 0xda, 0xee, 0x3d, 0x38, 0xee, 0x4a, 0x71, 0xcd, 0x67, 0xc3, 0x30, 0x62, 0x0b, 0x9a,
0x8d, 0xf6, 0x1e, 0xfe, 0xdf, 0x74, 0xdb, 0xd9, 0xce, 0xa1, 0x94, 0x6e, 0x05, 0x67, 0xab, 0xb4,
0xce, 0xfe, 0x3a, 0x9b, 0xd9, 0xa6, 0x67, 0xb7, 0xe9, 0x0d, 0x15, 0x0b, 0x03, 0xcc, 0x74, 0xef,
0x1c, 0xa8, 0x0f, 0x59, 0x62, 0xd4, 0x6d, 0xb9, 0x74, 0x80, 0x85, 0x9e, 0x29, 0x1a, 0x7e, 0x1a,
0x87, 0x18, 0xc0, 0x02, 0xd5, 0xa0, 0x66, 0xbd, 0x86, 0x26, 0x01, 0x54, 0xb1, 0x4c, 0x06, 0x15,
0xb0, 0x0b, 0x7f, 0x9b, 0x0d, 0x0f, 0xd2, 0x80, 0x2d, 0x5a, 0x11, 0xeb, 0x1f, 0xee, 0x47, 0xa8,
0xe4, 0x62, 0xe4, 0x12, 0xca, 0xd3, 0x98, 0xdf, 0xb0, 0xd8, 0x8e, 0xd8, 0xde, 0x5a, 0xfc, 0x02,
0xd3, 0x6c, 0x09, 0x2b, 0xe2, 0x8e, 0xe1, 0xe8, 0xb7, 0x20, 0x79, 0x00, 0xb5, 0xee, 0x9c, 0x33,
0x91, 0x5c, 0xd2, 0x2f, 0x57, 0x32, 0x4e, 0xb0, 0x54, 0x2d, 0xd8, 0x74, 0xe6, 0x28, 0x2e, 0x90,
0x2a, 0x6c, 0x50, 0xc6, 0x99, 0x1e, 0x66, 0x6e, 0x9b, 0xe6, 0x5f, 0x7a, 0xf4, 0x18, 0x60, 0x7d,
0x51, 0xa4, 0x02, 0xfb, 0x6f, 0x07, 0xaf, 0x06, 0xaf, 0xdf, 0x0d, 0xea, 0x7b, 0x04, 0xa0, 0x7c,
0x11, 0xf4, 0x47, 0xbd, 0xa0, 0x5e, 0x40, 0xbb, 0x37, 0xea, 0x77, 0x7b, 0xf5, 0x62, 0xeb, 0xae,
0x08, 0xd0, 0xa1, 0x9a, 0x99, 0x3c, 0xf2, 0x2d, 0x53, 0x48, 0x2f, 0x9b, 0xb4, 0xb7, 0xbf, 0xe1,
0xdc, 0xf7, 0xd1, 0x7c, 0xb6, 0x6b, 0x9a, 0x69, 0xdf, 0xdd, 0x23, 0xdf, 0x1d, 0xa8, 0xe6, 0xef,
0x8f, 0x3c, 0xdf, 0x46, 0xea, 0x0f, 0x87, 0xdc, 0x7c, 0xb1, 0x7b, 0xe2, 0xaa, 0x8b, 0xaf, 0x70,
0xb0, 0xda, 0x2d, 0x79, 0xba, 0x8d, 0xd0, 0xaf, 0x87, 0xdd, 0x6c, 0xef, 0x98, 0x95, 0xd5, 0xee,
0xec, 0x7f, 0xf8, 0x0f, 0x83, 0x93, 0x32, 0x3e, 0x4f, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x53,
0xfe, 0xaa, 0x3a, 0x07, 0x05, 0x00, 0x00,
var fileDescriptor_base_f2480776612a8fbd = []byte{
// 535 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x41, 0x8f, 0x12, 0x4d,
0x10, 0xdd, 0x01, 0x3e, 0x36, 0x14, 0xb0, 0x81, 0xe6, 0x33, 0x21, 0x24, 0x26, 0x64, 0xa2, 0x09,
0x31, 0x9b, 0x9e, 0x88, 0xa2, 0x1e, 0x57, 0x58, 0x0e, 0xc4, 0x2c, 0x6e, 0x06, 0x45, 0x63, 0x4c,
0x48, 0x33, 0xf4, 0x32, 0x1d, 0xa1, 0xbb, 0x9d, 0x1e, 0x36, 0xae, 0x89, 0x27, 0xcf, 0xfe, 0x22,
0x8f, 0xfe, 0x31, 0x33, 0xdd, 0x0d, 0x0c, 0xbb, 0x1a, 0xe1, 0x34, 0x45, 0xd5, 0x7b, 0xaf, 0xaa,
0x1e, 0xd5, 0x70, 0x5f, 0x2e, 0x56, 0x73, 0xc6, 0x95, 0x37, 0x25, 0x8a, 0x7a, 0x32, 0x12, 0xb1,
0xd0, 0x21, 0xd6, 0x21, 0x72, 0x43, 0xa2, 0x42, 0x16, 0x88, 0x48, 0x62, 0x2e, 0x96, 0x64, 0x86,
0x2d, 0x1c, 0x6f, 0x31, 0x8d, 0xb3, 0x39, 0x8b, 0xc3, 0xd5, 0x14, 0x07, 0x62, 0xe9, 0x6d, 0xe0,
0x9e, 0x86, 0x7b, 0x6b, 0x75, 0x15, 0x92, 0x88, 0xce, 0xbc, 0x30, 0x58, 0x28, 0x49, 0x83, 0xe4,
0x3b, 0x49, 0x02, 0xa3, 0xe0, 0xd6, 0xa0, 0x7a, 0xa9, 0x81, 0x03, 0x7e, 0x25, 0x7c, 0xfa, 0x79,
0x45, 0x55, 0xec, 0xfe, 0x72, 0x00, 0xa5, 0xb3, 0x4a, 0x0a, 0xae, 0x28, 0xea, 0x42, 0x2e, 0xbe,
0x91, 0xb4, 0xee, 0x34, 0x9d, 0xd6, 0x49, 0x1b, 0xe3, 0x7f, 0x0f, 0x88, 0x8d, 0xca, 0x9b, 0x1b,
0x49, 0x7d, 0xcd, 0x45, 0x18, 0x6a, 0x06, 0x36, 0x21, 0x92, 0x4d, 0xae, 0x69, 0xa4, 0x98, 0xe0,
0xaa, 0x9e, 0x69, 0x66, 0x5b, 0x05, 0xbf, 0x6a, 0x4a, 0x2f, 0x25, 0x1b, 0xdb, 0x02, 0x7a, 0x08,
0x27, 0x16, 0x6f, 0xb1, 0xf5, 0x6c, 0xd3, 0x69, 0x15, 0xfc, 0xb2, 0xc9, 0x5a, 0x1c, 0x42, 0x90,
0xe3, 0x64, 0x49, 0xeb, 0x39, 0x5d, 0xd4, 0xb1, 0x7b, 0x0f, 0x6a, 0x3d, 0xc1, 0xaf, 0xd8, 0x7c,
0x14, 0x84, 0x74, 0x49, 0xd6, 0xcb, 0xbd, 0x87, 0xff, 0x77, 0xd3, 0x76, 0xbb, 0x33, 0xc8, 0x25,
0xbe, 0xe8, 0xed, 0x8a, 0xed, 0xd3, 0xbf, 0x6e, 0x67, 0xfc, 0xc4, 0xd6, 0x4f, 0x3c, 0x92, 0x34,
0xf0, 0x35, 0xd3, 0xfd, 0xe9, 0x40, 0x65, 0x44, 0x63, 0xa3, 0x6e, 0xdb, 0x25, 0x0b, 0x2c, 0xd5,
0x5c, 0x92, 0xe0, 0xd3, 0x24, 0xd0, 0x05, 0xdd, 0xa0, 0xe4, 0x97, 0x6d, 0xd6, 0xa0, 0x91, 0x0f,
0x25, 0xdd, 0x66, 0x0d, 0xca, 0xe8, 0x29, 0xbc, 0x7d, 0x3c, 0x1e, 0x26, 0x05, 0xdb, 0xb4, 0xc8,
0xb7, 0x3f, 0xd0, 0x29, 0xa0, 0xbb, 0x5e, 0x5b, 0xff, 0x2a, 0xb7, 0xad, 0x76, 0x3f, 0x42, 0x31,
0xa5, 0x84, 0x2e, 0x20, 0x3f, 0x8b, 0xd8, 0x35, 0x8d, 0xac, 0x21, 0x9d, 0xbd, 0x47, 0x39, 0xd7,
0x34, 0x3b, 0x90, 0x15, 0x71, 0x27, 0x50, 0xbd, 0x53, 0x44, 0x0f, 0xa0, 0xdc, 0x5b, 0x30, 0xca,
0xe3, 0x0b, 0xf2, 0xe5, 0x52, 0x44, 0xb1, 0x6e, 0x55, 0xf6, 0x77, 0x93, 0x29, 0x14, 0xe3, 0x1a,
0x95, 0xd9, 0x41, 0x99, 0x64, 0x72, 0xc8, 0x29, 0xef, 0xcd, 0x7f, 0xfa, 0xe8, 0x31, 0xc0, 0xf6,
0x02, 0x51, 0x11, 0x8e, 0xdf, 0x0e, 0x5f, 0x0d, 0x5f, 0xbf, 0x1b, 0x56, 0x8e, 0x10, 0x40, 0xfe,
0xdc, 0x1f, 0x8c, 0xfb, 0x7e, 0x25, 0xa3, 0xe3, 0xfe, 0x78, 0xd0, 0xeb, 0x57, 0xb2, 0xed, 0x1f,
0x59, 0x80, 0x2e, 0x51, 0xd4, 0xf0, 0xd0, 0xb7, 0xb5, 0x42, 0xf2, 0x12, 0x50, 0x67, 0xff, 0x9b,
0x4f, 0xbd, 0xa7, 0xc6, 0xb3, 0x43, 0x69, 0x66, 0x7c, 0xf7, 0x08, 0x7d, 0x77, 0xa0, 0x94, 0xbe,
0x56, 0xf4, 0x7c, 0x1f, 0xa9, 0x3f, 0x9c, 0x7d, 0xe3, 0xc5, 0xe1, 0xc4, 0xcd, 0x14, 0x5f, 0xa1,
0xb0, 0xf1, 0x16, 0x3d, 0xdd, 0x47, 0xe8, 0xf6, 0x33, 0x68, 0x74, 0x0e, 0x64, 0xad, 0x7b, 0x77,
0x8f, 0x3f, 0xfc, 0xa7, 0x8b, 0xd3, 0xbc, 0xfe, 0x3c, 0xf9, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xcc,
0x26, 0x80, 0xcf, 0x37, 0x05, 0x00, 0x00,
}

View File

@@ -33,9 +33,9 @@ message PluginInfoResponse {
// type indicates what type of plugin this is.
PluginType type = 1;
// plugin_api_version indicates the version of the Nomad Plugin API
// this plugin is built against.
string plugin_api_version = 2;
// plugin_api_versions indicates the versions of the Nomad Plugin API
// this plugin supports.
repeated string plugin_api_versions = 2;
// plugin_version is the semver version of this individual plugin.
// This is divorce from Nomads development and versioning.
@@ -61,6 +61,9 @@ message SetConfigRequest {
// nomad_config is the nomad client configuration sent to all plugins.
NomadConfig nomad_config = 2;
// plugin_api_version is the api version to use.
string plugin_api_version = 3;
}
// NomadConfig is the client configuration sent to all plugins

View File

@@ -31,10 +31,10 @@ func (b *basePluginServer) PluginInfo(context.Context, *proto.PluginInfoRequest)
}
presp := &proto.PluginInfoResponse{
Type: ptype,
PluginApiVersion: resp.PluginApiVersion,
PluginVersion: resp.PluginVersion,
Name: resp.Name,
Type: ptype,
PluginApiVersions: resp.PluginApiVersions,
PluginVersion: resp.PluginVersion,
Name: resp.Name,
}
return presp, nil
@@ -61,7 +61,7 @@ func (b *basePluginServer) SetConfig(ctx context.Context, req *proto.SetConfigRe
// Client configuration is filtered based on plugin type
cfg := nomadConfigFromProto(req.GetNomadConfig())
filteredCfg := new(ClientAgentConfig)
filteredCfg := new(AgentConfig)
if cfg != nil {
switch info.Type {
@@ -70,8 +70,15 @@ func (b *basePluginServer) SetConfig(ctx context.Context, req *proto.SetConfigRe
}
}
// Build the config request
c := &Config{
ApiVersion: req.GetPluginApiVersion(),
PluginConfig: req.GetMsgpackConfig(),
AgentConfig: filteredCfg,
}
// Set the config
if err := b.impl.SetConfig(req.GetMsgpackConfig(), filteredCfg); err != nil {
if err := b.impl.SetConfig(c); err != nil {
return nil, fmt.Errorf("SetConfig failed: %v", err)
}

View File

@@ -48,7 +48,7 @@ type TestConfig struct {
type PluginInfoFn func() (*PluginInfoResponse, error)
type ConfigSchemaFn func() (*hclspec.Spec, error)
type SetConfigFn func([]byte, *ClientAgentConfig) error
type SetConfigFn func(*Config) error
// MockPlugin is used for testing.
// Each function can be set as a closure to make assertions about how data
@@ -61,8 +61,8 @@ type MockPlugin struct {
func (p *MockPlugin) PluginInfo() (*PluginInfoResponse, error) { return p.PluginInfoF() }
func (p *MockPlugin) ConfigSchema() (*hclspec.Spec, error) { return p.ConfigSchemaF() }
func (p *MockPlugin) SetConfig(data []byte, cfg *ClientAgentConfig) error {
return p.SetConfigF(data, cfg)
func (p *MockPlugin) SetConfig(cfg *Config) error {
return p.SetConfigF(cfg)
}
// Below are static implementations of the base plugin functions
@@ -89,5 +89,5 @@ func TestConfigSchema() ConfigSchemaFn {
// NoopSetConfig is a noop implementation of set config
func NoopSetConfig() SetConfigFn {
return func(_ []byte, _ *ClientAgentConfig) error { return nil }
return func(_ *Config) error { return nil }
}

View File

@@ -9,9 +9,8 @@ import (
"sync"
"time"
"github.com/hashicorp/nomad/helper"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/device"
"github.com/hashicorp/nomad/plugins/shared/hclspec"
@@ -38,10 +37,10 @@ const (
var (
// pluginInfo describes the plugin
pluginInfo = &base.PluginInfoResponse{
Type: base.PluginTypeDevice,
PluginApiVersion: "0.0.1", // XXX This should be an array and should be consts
PluginVersion: "0.1.0",
Name: pluginName,
Type: base.PluginTypeDevice,
PluginApiVersions: []string{device.ApiVersion010},
PluginVersion: "v0.1.0",
Name: pluginName,
}
// configSpec is the specification of the plugin's configuration
@@ -109,9 +108,9 @@ func (d *FsDevice) ConfigSchema() (*hclspec.Spec, error) {
}
// SetConfig is used to set the configuration of the plugin.
func (d *FsDevice) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (d *FsDevice) SetConfig(c *base.Config) error {
var config Config
if err := base.MsgPackDecode(data, &config); err != nil {
if err := base.MsgPackDecode(c.PluginConfig, &config); err != nil {
return err
}

View File

@@ -24,27 +24,30 @@ func TestDevicePlugin_PluginInfo(t *testing.T) {
t.Parallel()
require := require.New(t)
var (
apiVersions = []string{"v0.1.0", "v0.2.0"}
)
const (
apiVersion = "v0.1.0"
pluginVersion = "v0.2.1"
pluginName = "mock_device"
)
knownType := func() (*base.PluginInfoResponse, error) {
info := &base.PluginInfoResponse{
Type: base.PluginTypeDevice,
PluginApiVersion: apiVersion,
PluginVersion: pluginVersion,
Name: pluginName,
Type: base.PluginTypeDevice,
PluginApiVersions: apiVersions,
PluginVersion: pluginVersion,
Name: pluginName,
}
return info, nil
}
unknownType := func() (*base.PluginInfoResponse, error) {
info := &base.PluginInfoResponse{
Type: "bad",
PluginApiVersion: apiVersion,
PluginVersion: pluginVersion,
Name: pluginName,
Type: "bad",
PluginApiVersions: apiVersions,
PluginVersion: pluginVersion,
Name: pluginName,
}
return info, nil
}
@@ -74,7 +77,7 @@ func TestDevicePlugin_PluginInfo(t *testing.T) {
resp, err := impl.PluginInfo()
require.NoError(err)
require.Equal(apiVersion, resp.PluginApiVersion)
require.Equal(apiVersions, resp.PluginApiVersions)
require.Equal(pluginVersion, resp.PluginVersion)
require.Equal(pluginName, resp.Name)
require.Equal(base.PluginTypeDevice, resp.Type)
@@ -129,17 +132,17 @@ func TestDevicePlugin_SetConfig(t *testing.T) {
MockPlugin: &base.MockPlugin{
PluginInfoF: func() (*base.PluginInfoResponse, error) {
return &base.PluginInfoResponse{
Type: base.PluginTypeDevice,
PluginApiVersion: "v0.0.1",
PluginVersion: "v0.0.1",
Name: "mock_device",
Type: base.PluginTypeDevice,
PluginApiVersions: []string{"v0.0.1"},
PluginVersion: "v0.0.1",
Name: "mock_device",
}, nil
},
ConfigSchemaF: func() (*hclspec.Spec, error) {
return base.TestSpec, nil
},
SetConfigF: func(data []byte, cfg *base.ClientAgentConfig) error {
receivedData = data
SetConfigF: func(cfg *base.Config) error {
receivedData = cfg.PluginConfig
return nil
},
},
@@ -169,7 +172,7 @@ func TestDevicePlugin_SetConfig(t *testing.T) {
})
cdata, err := msgpack.Marshal(config, config.Type())
require.NoError(err)
require.NoError(impl.SetConfig(cdata, nil))
require.NoError(impl.SetConfig(&base.Config{PluginConfig: cdata}))
require.Equal(cdata, receivedData)
// Decode the value back

View File

@@ -0,0 +1,6 @@
package device
const (
// ApiVersion010 is the initial API version for the device plugins
ApiVersion010 = "v0.1.0"
)

View File

@@ -0,0 +1,6 @@
package drivers
const (
// ApiVersion010 is the initial API version for the device plugins
ApiVersion010 = "v0.1.0"
)

View File

@@ -51,10 +51,11 @@ func TestPluginLoaderWithOptions(t testing.T,
// Build the plugin loader
config := &loader.PluginLoaderConfig{
Logger: logger,
PluginDir: "",
Configs: configs,
InternalPlugins: internal,
Logger: logger,
PluginDir: "",
Configs: configs,
InternalPlugins: internal,
SupportedVersions: loader.AgentSupportedApiVersions,
}
l, err := loader.NewPluginLoader(config)
if err != nil {

View File

@@ -120,7 +120,7 @@ func (c *Device) Run(args []string) int {
}
c.spec = spec
if err := c.setConfig(spec, config, nil); err != nil {
if err := c.setConfig(spec, device.ApiVersion010, config, nil); err != nil {
c.logger.Error("failed to set config", "error", err)
return 1
}
@@ -188,7 +188,7 @@ func (c *Device) getSpec() (hcldec.Spec, error) {
return schema, nil
}
func (c *Device) setConfig(spec hcldec.Spec, config []byte, nmdCfg *base.ClientAgentConfig) error {
func (c *Device) setConfig(spec hcldec.Spec, apiVersion string, config []byte, nmdCfg *base.AgentConfig) error {
// Parse the config into hcl
configVal, err := hclConfigToInterface(config)
if err != nil {
@@ -216,8 +216,14 @@ func (c *Device) setConfig(spec hcldec.Spec, config []byte, nmdCfg *base.ClientA
return err
}
req := &base.Config{
PluginConfig: config,
AgentConfig: nmdCfg,
ApiVersion: apiVersion,
}
c.logger.Trace("msgpack config", "config", string(cdata))
if err := c.dev.SetConfig(cdata, nmdCfg); err != nil {
if err := c.dev.SetConfig(req); err != nil {
return err
}

View File

@@ -0,0 +1,15 @@
package loader
import (
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/device"
)
var (
// AgentSupportedApiVersions is the set of API versions supported by the
// Nomad agent by plugin type.
AgentSupportedApiVersions = map[string][]string{
base.PluginTypeDevice: {device.ApiVersion010},
base.PluginTypeDriver: {device.ApiVersion010},
}
)

View File

@@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"path/filepath"
"sort"
multierror "github.com/hashicorp/go-multierror"
plugin "github.com/hashicorp/go-plugin"
@@ -130,6 +131,18 @@ func (l *PluginLoader) initInternal(plugins map[PluginID]*InternalPluginConfig,
}
info.version = v
// Detect the plugin API version to use
av, err := l.selectApiVersion(i)
if err != nil {
multierror.Append(&mErr, fmt.Errorf("failed to validate API versions %v for internal plugin %s: %v", i.PluginApiVersions, k, err))
continue
}
if av == "" {
l.logger.Warn("skipping plugin because supported API versions for plugin and Nomad do not overlap", "plugin", k)
continue
}
info.apiVersion = av
// Get the config schema
schema, err := base.ConfigSchema()
if err != nil {
@@ -142,9 +155,66 @@ func (l *PluginLoader) initInternal(plugins map[PluginID]*InternalPluginConfig,
fingerprinted[k] = info
}
if err := mErr.ErrorOrNil(); err != nil {
return nil, err
}
return fingerprinted, nil
}
// selectApiVersion takes in PluginInfo and returns the highest compatable
// version or an error if the plugins response is malformed. If there is no
// overlap, an empty string is returned.
func (l *PluginLoader) selectApiVersion(i *base.PluginInfoResponse) (string, error) {
if i == nil {
return "", fmt.Errorf("nil plugin info given")
}
if len(i.PluginApiVersions) == 0 {
return "", fmt.Errorf("plugin provided no compatible API versions")
}
pluginVersions, err := convertVersions(i.PluginApiVersions)
if err != nil {
return "", fmt.Errorf("plugin provided invalid versions: %v", err)
}
// Lookup the supported versions. These will be sorted highest to lowest
supportedVersions, ok := l.supportedVersions[i.Type]
if !ok {
return "", fmt.Errorf("unsupported plugin type %q", i.Type)
}
for _, sv := range supportedVersions {
for _, pv := range pluginVersions {
if sv.Equal(pv) {
return pv.Original(), nil
}
}
}
return "", nil
}
// convertVersions takes a list of string versions and returns a sorted list of
// versions from highest to lowest.
func convertVersions(in []string) ([]*version.Version, error) {
converted := make([]*version.Version, len(in))
for i, v := range in {
vv, err := version.NewVersion(v)
if err != nil {
return nil, fmt.Errorf("failed to convert version %q : %v", v, err)
}
converted[i] = vv
}
sort.Slice(converted, func(i, j int) bool {
return converted[i].GreaterThan(converted[j])
})
return converted, nil
}
// scan scans the plugin directory and retrieves potentially eligible binaries
func (l *PluginLoader) scan() ([]os.FileInfo, error) {
if l.pluginDir == "" {
@@ -200,10 +270,14 @@ func (l *PluginLoader) fingerprintPlugins(plugins []os.FileInfo, configs map[str
c := configs[name]
info, err := l.fingerprintPlugin(p, c)
if err != nil {
l.logger.Error("failed to fingerprint plugin", "plugin", name)
l.logger.Error("failed to fingerprint plugin", "plugin", name, "error", err)
multierror.Append(&mErr, err)
continue
}
if info == nil {
// Plugin was skipped for validation reasons
continue
}
id := PluginID{
Name: info.baseInfo.Name,
@@ -297,6 +371,17 @@ func (l *PluginLoader) fingerprintPlugin(pluginExe os.FileInfo, config *config.P
}
info.version = v
// Detect the plugin API version to use
av, err := l.selectApiVersion(i)
if err != nil {
return nil, fmt.Errorf("failed to validate API versions %v for plugin %s (%v): %v", i.PluginApiVersions, i.Name, info.exePath, err)
}
if av == "" {
l.logger.Warn("skipping plugin because supported API versions for plugin and Nomad do not overlap", "plugin", i.Name, "path", info.exePath)
return nil, nil
}
info.apiVersion = av
// Retrieve the schema
schema, err := bplugin.ConfigSchema()
if err != nil {
@@ -404,12 +489,18 @@ func (l *PluginLoader) validePluginConfig(id PluginID, info *pluginInfo) error {
}
defer instance.Kill()
base, ok := instance.Plugin().(base.BasePlugin)
b, ok := instance.Plugin().(base.BasePlugin)
if !ok {
return fmt.Errorf("dispensed plugin %s doesn't meet base plugin interface", id)
}
if err := base.SetConfig(cdata, nil); err != nil {
c := &base.Config{
PluginConfig: cdata,
AgentConfig: nil,
ApiVersion: info.apiVersion,
}
if err := b.SetConfig(c); err != nil {
return fmt.Errorf("setting config on plugin failed: %v", err)
}
return nil

View File

@@ -22,11 +22,15 @@ type PluginInstance interface {
// Exited returns whether the plugin has exited
Exited() bool
// ApiVersion returns the API version to be used with the plugin
ApiVersion() string
}
// internalPluginInstance wraps an internal plugin
type internalPluginInstance struct {
instance interface{}
instance interface{}
apiVersion string
}
func (p *internalPluginInstance) Internal() bool { return true }
@@ -34,16 +38,19 @@ func (p *internalPluginInstance) Kill()
func (p *internalPluginInstance) ReattachConfig() (*plugin.ReattachConfig, bool) { return nil, false }
func (p *internalPluginInstance) Plugin() interface{} { return p.instance }
func (p *internalPluginInstance) Exited() bool { return false }
func (p *internalPluginInstance) ApiVersion() string { return p.apiVersion }
// externalPluginInstance wraps an external plugin
type externalPluginInstance struct {
client *plugin.Client
instance interface{}
client *plugin.Client
instance interface{}
apiVersion string
}
func (p *externalPluginInstance) Internal() bool { return false }
func (p *externalPluginInstance) Plugin() interface{} { return p.instance }
func (p *externalPluginInstance) Exited() bool { return p.client.Exited() }
func (p *externalPluginInstance) ApiVersion() string { return p.apiVersion }
func (p *externalPluginInstance) ReattachConfig() (*plugin.ReattachConfig, bool) {
return p.client.ReattachConfig(), true

View File

@@ -19,7 +19,7 @@ import (
type PluginCatalog interface {
// Dispense returns the plugin given its name and type. This will also
// configure the plugin
Dispense(name, pluginType string, config *base.ClientAgentConfig, logger log.Logger) (PluginInstance, error)
Dispense(name, pluginType string, config *base.AgentConfig, logger log.Logger) (PluginInstance, error)
// Reattach is used to reattach to a previously launched external plugin.
Reattach(name, pluginType string, config *plugin.ReattachConfig) (PluginInstance, error)
@@ -28,17 +28,10 @@ type PluginCatalog interface {
Catalog() map[string][]*base.PluginInfoResponse
}
// PluginLoader is used to retrieve plugins either externally or from internal
// factories.
type PluginLoader struct {
// logger is the plugin loaders logger
logger log.Logger
// pluginDir is the directory containing plugin binaries
pluginDir string
// plugins maps a plugin to information required to launch it
plugins map[PluginID]*pluginInfo
// InternalPluginConfig is used to configure launching an internal plugin.
type InternalPluginConfig struct {
Config map[string]interface{}
Factory plugins.PluginFactory
}
// PluginID is a tuple identifying a plugin
@@ -75,12 +68,25 @@ type PluginLoaderConfig struct {
// InternalPlugins allows registering internal plugins.
InternalPlugins map[PluginID]*InternalPluginConfig
// SupportedVersions is a mapping of plugin type to the supported versions
SupportedVersions map[string][]string
}
// InternalPluginConfig is used to configure launching an internal plugin.
type InternalPluginConfig struct {
Config map[string]interface{}
Factory plugins.PluginFactory
// PluginLoader is used to retrieve plugins either externally or from internal
// factories.
type PluginLoader struct {
// logger is the plugin loaders logger
logger log.Logger
// supportedVersions is a mapping of plugin type to the supported versions
supportedVersions map[string][]*version.Version
// pluginDir is the directory containing plugin binaries
pluginDir string
// plugins maps a plugin to information required to launch it
plugins map[PluginID]*pluginInfo
}
// pluginInfo captures the necessary information to launch and configure a
@@ -91,8 +97,9 @@ type pluginInfo struct {
exePath string
args []string
baseInfo *base.PluginInfoResponse
version *version.Version
baseInfo *base.PluginInfoResponse
version *version.Version
apiVersion string
configSchema *hclspec.Spec
config map[string]interface{}
@@ -106,11 +113,22 @@ func NewPluginLoader(config *PluginLoaderConfig) (*PluginLoader, error) {
return nil, fmt.Errorf("invalid plugin loader configuration passed: %v", err)
}
// Convert the versions
supportedVersions := make(map[string][]*version.Version, len(config.SupportedVersions))
for pType, versions := range config.SupportedVersions {
converted, err := convertVersions(versions)
if err != nil {
return nil, err
}
supportedVersions[pType] = converted
}
logger := config.Logger.Named("plugin_loader").With("plugin_dir", config.PluginDir)
l := &PluginLoader{
logger: logger,
pluginDir: config.PluginDir,
plugins: make(map[PluginID]*pluginInfo),
logger: logger,
supportedVersions: supportedVersions,
pluginDir: config.PluginDir,
plugins: make(map[PluginID]*pluginInfo),
}
if err := l.init(config); err != nil {
@@ -122,7 +140,7 @@ func NewPluginLoader(config *PluginLoaderConfig) (*PluginLoader, error) {
// Dispense returns a plugin instance, loading it either internally or by
// launching an external plugin.
func (l *PluginLoader) Dispense(name, pluginType string, config *base.ClientAgentConfig, logger log.Logger) (PluginInstance, error) {
func (l *PluginLoader) Dispense(name, pluginType string, config *base.AgentConfig, logger log.Logger) (PluginInstance, error) {
id := PluginID{
Name: name,
PluginType: pluginType,
@@ -136,26 +154,31 @@ func (l *PluginLoader) Dispense(name, pluginType string, config *base.ClientAgen
var instance PluginInstance
if pinfo.factory != nil {
instance = &internalPluginInstance{
instance: pinfo.factory(logger),
instance: pinfo.factory(logger),
apiVersion: pinfo.apiVersion,
}
} else {
var err error
instance, err = l.dispensePlugin(pinfo.baseInfo.Type, pinfo.exePath, pinfo.args, nil, logger)
instance, err = l.dispensePlugin(pinfo.baseInfo.Type, pinfo.apiVersion, pinfo.exePath, pinfo.args, nil, logger)
if err != nil {
return nil, fmt.Errorf("failed to launch plugin: %v", err)
}
}
// Cast to the base type and set the config
base, ok := instance.Plugin().(base.BasePlugin)
b, ok := instance.Plugin().(base.BasePlugin)
if !ok {
return nil, fmt.Errorf("plugin %s doesn't implement base plugin interface", id)
}
if len(pinfo.msgpackConfig) != 0 {
if err := base.SetConfig(pinfo.msgpackConfig, config); err != nil {
return nil, fmt.Errorf("setting config for plugin %s failed: %v", id, err)
}
c := &base.Config{
PluginConfig: pinfo.msgpackConfig,
AgentConfig: config,
ApiVersion: pinfo.apiVersion,
}
if err := b.SetConfig(c); err != nil {
return nil, fmt.Errorf("setting config for plugin %s failed: %v", id, err)
}
return instance, nil
@@ -163,12 +186,12 @@ func (l *PluginLoader) Dispense(name, pluginType string, config *base.ClientAgen
// Reattach reattaches to a previously launched external plugin.
func (l *PluginLoader) Reattach(name, pluginType string, config *plugin.ReattachConfig) (PluginInstance, error) {
return l.dispensePlugin(pluginType, "", nil, config, l.logger)
return l.dispensePlugin(pluginType, "", "", nil, config, l.logger)
}
// dispensePlugin is used to launch or reattach to an external plugin.
func (l *PluginLoader) dispensePlugin(
pluginType, cmd string, args []string, reattach *plugin.ReattachConfig,
pluginType, apiVersion, cmd string, args []string, reattach *plugin.ReattachConfig,
logger log.Logger) (PluginInstance, error) {
var pluginCmd *exec.Cmd
@@ -207,6 +230,31 @@ func (l *PluginLoader) dispensePlugin(
client: client,
instance: raw,
}
if apiVersion != "" {
instance.apiVersion = apiVersion
} else {
// We do not know the API version since we are reattaching, so discover
// it
bplugin := raw.(base.BasePlugin)
// Retrieve base plugin information
i, err := bplugin.PluginInfo()
if err != nil {
return nil, fmt.Errorf("failed to get plugin info for plugin: %v", err)
}
apiVersion, err := l.selectApiVersion(i)
if err != nil {
return nil, fmt.Errorf("failed to validate API versions %v for plugin %s: %v", i.PluginApiVersions, i.Name, err)
}
if apiVersion == "" {
return nil, fmt.Errorf("failed to reattach to plugin because supported API versions for the plugin and Nomad do not overlap")
}
instance.apiVersion = apiVersion
}
return instance, nil
}

View File

@@ -6,9 +6,11 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"testing"
log "github.com/hashicorp/go-hclog"
version "github.com/hashicorp/go-version"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/nomad/structs/config"
"github.com/hashicorp/nomad/plugins/base"
@@ -16,6 +18,14 @@ import (
"github.com/stretchr/testify/require"
)
var (
// supportedApiVersions is the set of api versions that the "client" can
// support
supportedApiVersions = map[string][]string{
base.PluginTypeDevice: {device.ApiVersion010},
}
)
// harness is used to build a temp directory and copy our own test executable
// into it, allowing the plugin loader to scan for plugins.
type harness struct {
@@ -104,18 +114,21 @@ func TestPluginLoader_External(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[0],
"-api-version", device.ApiVersion010},
},
{
Name: plugins[1],
Args: []string{"-plugin", "-name", plugins[1],
"-type", base.PluginTypeDevice, "-version", pluginVersions[1]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[1],
"-api-version", device.ApiVersion010, "-api-version", "v0.2.0"},
},
},
}
@@ -133,21 +146,155 @@ func TestPluginLoader_External(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersions: []string{"v0.1.0"},
},
{
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{"v0.1.0", "v0.2.0"},
},
}
require.EqualValues(expected, detected)
}
func TestPluginLoader_External_ApiVersions(t *testing.T) {
t.Parallel()
require := require.New(t)
// Create two plugins
plugins := []string{"mock-device", "mock-device-2", "mock-device-3"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
h := newHarness(t, plugins)
defer h.cleanup()
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: map[string][]string{
base.PluginTypeDevice: {"0.2.0", "0.2.1", "0.3.0"},
},
Configs: []*config.PluginConfig{
{
// No supporting version
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0],
"-api-version", "v0.1.0"},
},
{
// Pick highest matching
Name: plugins[1],
Args: []string{"-plugin", "-name", plugins[1],
"-type", base.PluginTypeDevice, "-version", pluginVersions[1],
"-api-version", "v0.1.0",
"-api-version", "v0.2.0",
"-api-version", "v0.2.1",
"-api-version", "v0.2.2",
},
},
{
// Pick highest matching
Name: plugins[2],
Args: []string{"-plugin", "-name", plugins[2],
"-type", base.PluginTypeDevice, "-version", pluginVersions[1],
"-api-version", "v0.1.0",
"-api-version", "v0.2.0",
"-api-version", "v0.2.1",
"-api-version", "v0.3.0",
},
},
},
}
l, err := NewPluginLoader(lconfig)
require.NoError(err)
// Get the catalog and assert we have the two plugins
c := l.Catalog()
require.Len(c, 1)
require.Contains(c, base.PluginTypeDevice)
detected := c[base.PluginTypeDevice]
require.Len(detected, 2)
sort.Slice(detected, func(i, j int) bool { return detected[i].Name < detected[j].Name })
expected := []*base.PluginInfoResponse{
{
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{"v0.1.0", "v0.2.0", "v0.2.1", "v0.2.2"},
},
{
Name: plugins[2],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{"v0.1.0", "v0.2.0", "v0.2.1", "v0.3.0"},
},
}
require.EqualValues(expected, detected)
// Test we chose the correct versions by dispensing and checking and then
// reattaching and checking
p1, err := l.Dispense(plugins[1], base.PluginTypeDevice, nil, logger)
require.NoError(err)
defer p1.Kill()
require.Equal("v0.2.1", p1.ApiVersion())
p2, err := l.Dispense(plugins[2], base.PluginTypeDevice, nil, logger)
require.NoError(err)
defer p2.Kill()
require.Equal("v0.3.0", p2.ApiVersion())
// Test reattach api versions
rc1, ok := p1.ReattachConfig()
require.True(ok)
r1, err := l.Reattach(plugins[1], base.PluginTypeDriver, rc1)
require.NoError(err)
require.Equal("v0.2.1", r1.ApiVersion())
rc2, ok := p2.ReattachConfig()
require.True(ok)
r2, err := l.Reattach(plugins[2], base.PluginTypeDriver, rc2)
require.NoError(err)
require.Equal("v0.3.0", r2.ApiVersion())
}
func TestPluginLoader_External_NoApiVersion(t *testing.T) {
t.Parallel()
require := require.New(t)
// Create two plugins
plugins := []string{"mock-device"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
h := newHarness(t, plugins)
defer h.cleanup()
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
},
},
}
_, err := NewPluginLoader(lconfig)
require.Error(err)
require.Contains(err.Error(), "no compatible API versions")
}
func TestPluginLoader_External_Config(t *testing.T) {
t.Parallel()
require := require.New(t)
@@ -161,13 +308,14 @@ func TestPluginLoader_External_Config(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[0], "-api-version", device.ApiVersion010},
Config: map[string]interface{}{
"foo": "1",
"bar": "2",
@@ -176,7 +324,7 @@ func TestPluginLoader_External_Config(t *testing.T) {
{
Name: plugins[1],
Args: []string{"-plugin", "-name", plugins[1],
"-type", base.PluginTypeDevice, "-version", pluginVersions[1]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[1], "-api-version", device.ApiVersion010},
Config: map[string]interface{}{
"foo": "3",
"bar": "4",
@@ -198,16 +346,16 @@ func TestPluginLoader_External_Config(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersions: []string{device.ApiVersion010},
},
{
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
@@ -227,13 +375,14 @@ func TestPluginLoader_External_Config_Bad(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[0], "-api-version", device.ApiVersion010},
Config: map[string]interface{}{
"foo": "1",
"bar": "2",
@@ -261,18 +410,19 @@ func TestPluginLoader_External_VersionOverlap(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[0], "-api-version", device.ApiVersion010},
},
{
Name: plugins[1],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[1]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[1], "-api-version", device.ApiVersion010},
},
},
}
@@ -290,10 +440,10 @@ func TestPluginLoader_External_VersionOverlap(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
@@ -309,24 +459,26 @@ func TestPluginLoader_Internal(t *testing.T) {
plugins := []string{"mock-device", "mock-device-2"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
pluginApiVersions := []string{device.ApiVersion010}
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
{
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], true),
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], pluginApiVersions, true),
},
{
Name: plugins[1],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[1], base.PluginTypeDevice, pluginVersions[1], true),
Factory: mockFactory(plugins[1], base.PluginTypeDevice, pluginVersions[1], pluginApiVersions, true),
},
},
}
@@ -344,21 +496,134 @@ func TestPluginLoader_Internal(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersions: []string{device.ApiVersion010},
},
{
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
}
func TestPluginLoader_Internal_ApiVersions(t *testing.T) {
t.Parallel()
require := require.New(t)
// Create two plugins
plugins := []string{"mock-device", "mock-device-2", "mock-device-3"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
h := newHarness(t, nil)
defer h.cleanup()
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: map[string][]string{
base.PluginTypeDevice: {"0.2.0", "0.2.1", "0.3.0"},
},
InternalPlugins: map[PluginID]*InternalPluginConfig{
{
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], []string{"v0.1.0"}, true),
},
{
Name: plugins[1],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[1], base.PluginTypeDevice, pluginVersions[1],
[]string{"v0.1.0", "v0.2.0", "v0.2.1", "v0.2.2"}, true),
},
{
Name: plugins[2],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[2], base.PluginTypeDevice, pluginVersions[1],
[]string{"v0.1.0", "v0.2.0", "v0.2.1", "v0.3.0"}, true),
},
},
}
l, err := NewPluginLoader(lconfig)
require.NoError(err)
// Get the catalog and assert we have the two plugins
c := l.Catalog()
require.Len(c, 1)
require.Contains(c, base.PluginTypeDevice)
detected := c[base.PluginTypeDevice]
require.Len(detected, 2)
sort.Slice(detected, func(i, j int) bool { return detected[i].Name < detected[j].Name })
expected := []*base.PluginInfoResponse{
{
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{"v0.1.0", "v0.2.0", "v0.2.1", "v0.2.2"},
},
{
Name: plugins[2],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{"v0.1.0", "v0.2.0", "v0.2.1", "v0.3.0"},
},
}
require.EqualValues(expected, detected)
// Test we chose the correct versions by dispensing and checking and then
// reattaching and checking
p1, err := l.Dispense(plugins[1], base.PluginTypeDevice, nil, logger)
require.NoError(err)
defer p1.Kill()
require.Equal("v0.2.1", p1.ApiVersion())
p2, err := l.Dispense(plugins[2], base.PluginTypeDevice, nil, logger)
require.NoError(err)
defer p2.Kill()
require.Equal("v0.3.0", p2.ApiVersion())
}
func TestPluginLoader_Internal_NoApiVersion(t *testing.T) {
t.Parallel()
require := require.New(t)
// Create two plugins
plugins := []string{"mock-device"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
h := newHarness(t, nil)
defer h.cleanup()
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
{
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], nil, true),
},
},
}
_, err := NewPluginLoader(lconfig)
require.Error(err)
require.Contains(err.Error(), "no compatible API versions")
}
func TestPluginLoader_Internal_Config(t *testing.T) {
t.Parallel()
require := require.New(t)
@@ -369,18 +634,20 @@ func TestPluginLoader_Internal_Config(t *testing.T) {
plugins := []string{"mock-device", "mock-device-2"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
pluginApiVersions := []string{device.ApiVersion010}
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
{
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], true),
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], pluginApiVersions, true),
Config: map[string]interface{}{
"foo": "1",
"bar": "2",
@@ -390,7 +657,7 @@ func TestPluginLoader_Internal_Config(t *testing.T) {
Name: plugins[1],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[1], base.PluginTypeDevice, pluginVersions[1], true),
Factory: mockFactory(plugins[1], base.PluginTypeDevice, pluginVersions[1], pluginApiVersions, true),
Config: map[string]interface{}{
"foo": "3",
"bar": "4",
@@ -412,16 +679,16 @@ func TestPluginLoader_Internal_Config(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersions: []string{device.ApiVersion010},
},
{
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[1],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
@@ -438,6 +705,7 @@ func TestPluginLoader_Internal_ExternalConfig(t *testing.T) {
plugin := "mock-device"
pluginVersion := "v0.0.1"
pluginApiVersions := []string{device.ApiVersion010}
id := PluginID{
Name: plugin,
@@ -451,11 +719,12 @@ func TestPluginLoader_Internal_ExternalConfig(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
id: {
Factory: mockFactory(plugin, base.PluginTypeDevice, pluginVersion, true),
Factory: mockFactory(plugin, base.PluginTypeDevice, pluginVersion, pluginApiVersions, true),
Config: map[string]interface{}{
"foo": "1",
"bar": "2",
@@ -482,10 +751,10 @@ func TestPluginLoader_Internal_ExternalConfig(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugin,
Type: base.PluginTypeDevice,
PluginVersion: pluginVersion,
PluginApiVersion: "v0.1.0",
Name: plugin,
Type: base.PluginTypeDevice,
PluginVersion: pluginVersion,
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
@@ -507,18 +776,20 @@ func TestPluginLoader_Internal_Config_Bad(t *testing.T) {
plugins := []string{"mock-device"}
pluginVersions := []string{"v0.0.1"}
pluginApiVersions := []string{device.ApiVersion010}
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
{
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], true),
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], pluginApiVersions, true),
Config: map[string]interface{}{
"foo": "1",
"bar": "2",
@@ -540,19 +811,22 @@ func TestPluginLoader_InternalOverrideExternal(t *testing.T) {
// Create two plugins
plugins := []string{"mock-device"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
pluginApiVersions := []string{device.ApiVersion010}
h := newHarness(t, plugins)
defer h.cleanup()
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[0], "-api-version", pluginApiVersions[0]},
},
},
InternalPlugins: map[PluginID]*InternalPluginConfig{
@@ -560,7 +834,7 @@ func TestPluginLoader_InternalOverrideExternal(t *testing.T) {
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[1], true),
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[1], pluginApiVersions, true),
},
},
}
@@ -578,10 +852,10 @@ func TestPluginLoader_InternalOverrideExternal(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
@@ -594,19 +868,22 @@ func TestPluginLoader_ExternalOverrideInternal(t *testing.T) {
// Create two plugins
plugins := []string{"mock-device"}
pluginVersions := []string{"v0.0.1", "v0.0.2"}
pluginApiVersions := []string{device.ApiVersion010}
h := newHarness(t, plugins)
defer h.cleanup()
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[1]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[1], "-api-version", pluginApiVersions[0]},
},
},
InternalPlugins: map[PluginID]*InternalPluginConfig{
@@ -614,7 +891,7 @@ func TestPluginLoader_ExternalOverrideInternal(t *testing.T) {
Name: plugins[0],
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], true),
Factory: mockFactory(plugins[0], base.PluginTypeDevice, pluginVersions[0], pluginApiVersions, true),
},
},
}
@@ -632,10 +909,10 @@ func TestPluginLoader_ExternalOverrideInternal(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[1],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
@@ -656,13 +933,14 @@ func TestPluginLoader_Dispense_External(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugin,
Args: []string{"-plugin", "-name", plugin,
"-type", base.PluginTypeDevice, "-version", pluginVersion},
"-type", base.PluginTypeDevice, "-version", pluginVersion, "-api-version", device.ApiVersion010},
Config: map[string]interface{}{
"res_key": expKey,
},
@@ -694,11 +972,12 @@ func TestPluginLoader_Dispense_Internal(t *testing.T) {
// Create two plugins
plugin := "mock-device"
pluginVersion := "v0.0.1"
pluginApiVersions := []string{device.ApiVersion010}
h := newHarness(t, nil)
defer h.cleanup()
expKey := "set_config_worked"
expNomadConfig := &base.ClientAgentConfig{
expNomadConfig := &base.AgentConfig{
Driver: &base.ClientDriverConfig{
ClientMinPort: 100,
},
@@ -707,14 +986,15 @@ func TestPluginLoader_Dispense_Internal(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
{
Name: plugin,
PluginType: base.PluginTypeDevice,
}: {
Factory: mockFactory(plugin, base.PluginTypeDevice, pluginVersion, true),
Factory: mockFactory(plugin, base.PluginTypeDevice, pluginVersion, pluginApiVersions, true),
Config: map[string]interface{}{
"res_key": expKey,
},
@@ -741,6 +1021,7 @@ func TestPluginLoader_Dispense_Internal(t *testing.T) {
mock, ok := p.Plugin().(*mockPlugin)
require.True(ok)
require.Exactly(expNomadConfig, mock.nomadConfig)
require.Equal(device.ApiVersion010, mock.negotiatedApiVersion)
}
func TestPluginLoader_Dispense_NoConfigSchema_External(t *testing.T) {
@@ -758,13 +1039,14 @@ func TestPluginLoader_Dispense_NoConfigSchema_External(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugin,
Args: []string{"-plugin", "-config-schema=false", "-name", plugin,
"-type", base.PluginTypeDevice, "-version", pluginVersion},
"-type", base.PluginTypeDevice, "-version", pluginVersion, "-api-version", device.ApiVersion010},
Config: map[string]interface{}{
"res_key": expKey,
},
@@ -797,6 +1079,7 @@ func TestPluginLoader_Dispense_NoConfigSchema_Internal(t *testing.T) {
// Create two plugins
plugin := "mock-device"
pluginVersion := "v0.0.1"
pluginApiVersions := []string{device.ApiVersion010}
h := newHarness(t, nil)
defer h.cleanup()
@@ -809,11 +1092,12 @@ func TestPluginLoader_Dispense_NoConfigSchema_Internal(t *testing.T) {
PluginType: base.PluginTypeDevice,
}
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
InternalPlugins: map[PluginID]*InternalPluginConfig{
pid: {
Factory: mockFactory(plugin, base.PluginTypeDevice, pluginVersion, false),
Factory: mockFactory(plugin, base.PluginTypeDevice, pluginVersion, pluginApiVersions, false),
Config: map[string]interface{}{
"res_key": expKey,
},
@@ -826,7 +1110,7 @@ func TestPluginLoader_Dispense_NoConfigSchema_Internal(t *testing.T) {
require.Contains(err.Error(), "configuration not allowed")
// Remove the config and try again
lconfig.InternalPlugins[pid].Factory = mockFactory(plugin, base.PluginTypeDevice, pluginVersion, true)
lconfig.InternalPlugins[pid].Factory = mockFactory(plugin, base.PluginTypeDevice, pluginVersion, pluginApiVersions, true)
l, err := NewPluginLoader(lconfig)
require.NoError(err)
@@ -854,13 +1138,14 @@ func TestPluginLoader_Reattach_External(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugin,
Args: []string{"-plugin", "-name", plugin,
"-type", base.PluginTypeDevice, "-version", pluginVersion},
"-type", base.PluginTypeDevice, "-version", pluginVersion, "-api-version", device.ApiVersion010},
Config: map[string]interface{}{
"res_key": expKey,
},
@@ -914,8 +1199,9 @@ func TestPluginLoader_Bad_Executable(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugin,
@@ -956,13 +1242,14 @@ func TestPluginLoader_External_SkipBadFiles(t *testing.T) {
logger := testlog.HCLogger(t)
logger.SetLevel(log.Trace)
lconfig := &PluginLoaderConfig{
Logger: logger,
PluginDir: h.pluginDir(),
Logger: logger,
PluginDir: h.pluginDir(),
SupportedVersions: supportedApiVersions,
Configs: []*config.PluginConfig{
{
Name: plugins[0],
Args: []string{"-plugin", "-name", plugins[0],
"-type", base.PluginTypeDevice, "-version", pluginVersions[0]},
"-type", base.PluginTypeDevice, "-version", pluginVersions[0], "-api-version", device.ApiVersion010},
},
},
}
@@ -980,11 +1267,55 @@ func TestPluginLoader_External_SkipBadFiles(t *testing.T) {
expected := []*base.PluginInfoResponse{
{
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersion: "v0.1.0",
Name: plugins[0],
Type: base.PluginTypeDevice,
PluginVersion: pluginVersions[0],
PluginApiVersions: []string{device.ApiVersion010},
},
}
require.EqualValues(expected, detected)
}
func TestPluginLoader_ConvertVersions(t *testing.T) {
v010 := version.Must(version.NewVersion("v0.1.0"))
v020 := version.Must(version.NewVersion("v0.2.0"))
v021 := version.Must(version.NewVersion("v0.2.1"))
v030 := version.Must(version.NewVersion("v0.3.0"))
cases := []struct {
in []string
out []*version.Version
err bool
}{
{
in: []string{"v0.1.0", "0.2.0", "v0.2.1"},
out: []*version.Version{v021, v020, v010},
},
{
in: []string{"0.3.0", "v0.1.0", "0.2.0", "v0.2.1"},
out: []*version.Version{v030, v021, v020, v010},
},
{
in: []string{"foo", "v0.1.0", "0.2.0", "v0.2.1"},
err: true,
},
}
for _, c := range cases {
t.Run(strings.Join(c.in, ","), func(t *testing.T) {
act, err := convertVersions(c.in)
if err != nil {
if c.err {
return
}
t.Fatalf("unexpected err: %v", err)
}
require.Len(t, act, len(c.out))
for i, v := range act {
if !v.Equal(c.out[i]) {
t.Fatalf("parsed version[%d] not equal: %v != %v", i, v, c.out[i])
}
}
})
}
}

View File

@@ -15,20 +15,33 @@ import (
"github.com/hashicorp/nomad/plugins/shared/hclspec"
)
type stringSliceFlags []string
func (i *stringSliceFlags) String() string {
return "my string representation"
}
func (i *stringSliceFlags) Set(value string) error {
*i = append(*i, value)
return nil
}
// TestMain runs either the tests or runs a mock plugin based on the passed
// flags
func TestMain(m *testing.M) {
var plugin, configSchema bool
var name, pluginType, pluginVersion string
var pluginApiVersions stringSliceFlags
flag.BoolVar(&plugin, "plugin", false, "run binary as a plugin")
flag.BoolVar(&configSchema, "config-schema", true, "return a config schema")
flag.StringVar(&name, "name", "", "plugin name")
flag.StringVar(&pluginType, "type", "", "plugin type")
flag.StringVar(&pluginVersion, "version", "", "plugin version")
flag.Var(&pluginApiVersions, "api-version", "supported plugin API version")
flag.Parse()
if plugin {
if err := pluginMain(name, pluginType, pluginVersion, configSchema); err != nil {
if err := pluginMain(name, pluginType, pluginVersion, pluginApiVersions, configSchema); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
@@ -38,7 +51,7 @@ func TestMain(m *testing.M) {
}
// pluginMain starts a mock plugin using the passed parameters
func pluginMain(name, pluginType, version string, config bool) error {
func pluginMain(name, pluginType, version string, apiVersions []string, config bool) error {
// Validate passed parameters
if name == "" || pluginType == "" {
return fmt.Errorf("name and plugin type must be specified")
@@ -55,6 +68,7 @@ func pluginMain(name, pluginType, version string, config bool) error {
name: name,
ptype: pluginType,
version: version,
apiVersions: apiVersions,
configSchema: config,
}
@@ -79,12 +93,13 @@ func pluginMain(name, pluginType, version string, config bool) error {
// mockFactory returns a PluginFactory method which creates the mock plugin with
// the passed parameters
func mockFactory(name, ptype, version string, configSchema bool) func(log log.Logger) interface{} {
func mockFactory(name, ptype, version string, apiVersions []string, configSchema bool) func(log log.Logger) interface{} {
return func(log log.Logger) interface{} {
return &mockPlugin{
name: name,
ptype: ptype,
version: version,
apiVersions: apiVersions,
configSchema: configSchema,
}
}
@@ -96,12 +111,18 @@ type mockPlugin struct {
name string
ptype string
version string
apiVersions []string
configSchema bool
// config is built on SetConfig
config *mockPluginConfig
// nomadconfig is set on SetConfig
nomadConfig *base.ClientAgentConfig
nomadConfig *base.AgentConfig
// negotiatedApiVersion is the version of the api to use and is set on
// SetConfig
negotiatedApiVersion string
}
// mockPluginConfig is the configuration for the mock plugin
@@ -118,10 +139,10 @@ type mockPluginConfig struct {
// building the mock plugin
func (m *mockPlugin) PluginInfo() (*base.PluginInfoResponse, error) {
return &base.PluginInfoResponse{
Type: m.ptype,
PluginApiVersion: "v0.1.0",
PluginVersion: m.version,
Name: m.name,
Type: m.ptype,
PluginApiVersions: m.apiVersions,
PluginVersion: m.version,
Name: m.name,
}, nil
}
@@ -141,14 +162,17 @@ func (m *mockPlugin) ConfigSchema() (*hclspec.Spec, error) {
}
// SetConfig decodes the configuration and stores it
func (m *mockPlugin) SetConfig(data []byte, cfg *base.ClientAgentConfig) error {
func (m *mockPlugin) SetConfig(c *base.Config) error {
var config mockPluginConfig
if err := base.MsgPackDecode(data, &config); err != nil {
return err
if len(c.PluginConfig) != 0 {
if err := base.MsgPackDecode(c.PluginConfig, &config); err != nil {
return err
}
}
m.config = &config
m.nomadConfig = cfg
m.nomadConfig = c.AgentConfig
m.negotiatedApiVersion = c.ApiVersion
return nil
}

View File

@@ -12,12 +12,12 @@ import (
// MockCatalog provides a mock PluginCatalog to be used for testing
type MockCatalog struct {
DispenseF func(name, pluginType string, cfg *base.ClientAgentConfig, logger log.Logger) (PluginInstance, error)
DispenseF func(name, pluginType string, cfg *base.AgentConfig, logger log.Logger) (PluginInstance, error)
ReattachF func(name, pluginType string, config *plugin.ReattachConfig) (PluginInstance, error)
CatalogF func() map[string][]*base.PluginInfoResponse
}
func (m *MockCatalog) Dispense(name, pluginType string, cfg *base.ClientAgentConfig, logger log.Logger) (PluginInstance, error) {
func (m *MockCatalog) Dispense(name, pluginType string, cfg *base.AgentConfig, logger log.Logger) (PluginInstance, error) {
return m.DispenseF(name, pluginType, cfg, logger)
}
@@ -36,6 +36,7 @@ type MockInstance struct {
ReattachConfigF func() (*plugin.ReattachConfig, bool)
PluginF func() interface{}
ExitedF func() bool
ApiVersionF func() string
}
func (m *MockInstance) Internal() bool { return m.InternalPlugin }
@@ -43,11 +44,12 @@ func (m *MockInstance) Kill() { m.KillF
func (m *MockInstance) ReattachConfig() (*plugin.ReattachConfig, bool) { return m.ReattachConfigF() }
func (m *MockInstance) Plugin() interface{} { return m.PluginF() }
func (m *MockInstance) Exited() bool { return m.ExitedF() }
func (m *MockInstance) ApiVersion() string { return m.ApiVersionF() }
// MockBasicExternalPlugin returns a MockInstance that simulates an external
// plugin returning it has been exited after kill is called. It returns the
// passed inst as the plugin
func MockBasicExternalPlugin(inst interface{}) *MockInstance {
func MockBasicExternalPlugin(inst interface{}, apiVersion string) *MockInstance {
var killedLock sync.Mutex
killed := helper.BoolToPtr(false)
return &MockInstance{
@@ -79,5 +81,7 @@ func MockBasicExternalPlugin(inst interface{}) *MockInstance {
defer killedLock.Unlock()
return *killed
},
ApiVersionF: func() string { return apiVersion },
}
}

View File

@@ -50,7 +50,7 @@ func (s *SingletonLoader) Catalog() map[string][]*base.PluginInfoResponse {
// Dispense returns the plugin given its name and type. This will also
// configure the plugin. If there is an instance of an already running plugin,
// this is used.
func (s *SingletonLoader) Dispense(name, pluginType string, config *base.ClientAgentConfig, logger log.Logger) (loader.PluginInstance, error) {
func (s *SingletonLoader) Dispense(name, pluginType string, config *base.AgentConfig, logger log.Logger) (loader.PluginInstance, error) {
return s.getPlugin(false, name, pluginType, logger, config, nil)
}
@@ -62,7 +62,7 @@ func (s *SingletonLoader) Reattach(name, pluginType string, config *plugin.Reatt
// getPlugin is a helper that either dispenses or reattaches to a plugin using
// futures to ensure only a single instance is retrieved
func (s *SingletonLoader) getPlugin(reattach bool, name, pluginType string, logger log.Logger,
nomadConfig *base.ClientAgentConfig, config *plugin.ReattachConfig) (loader.PluginInstance, error) {
nomadConfig *base.AgentConfig, config *plugin.ReattachConfig) (loader.PluginInstance, error) {
// Lock the instance map to prevent races
s.instanceLock.Lock()
@@ -102,7 +102,7 @@ func (s *SingletonLoader) getPlugin(reattach bool, name, pluginType string, logg
// dispense should be called in a go routine to not block and creates the
// desired plugin, setting the results in the future.
func (s *SingletonLoader) dispense(f *future, name, pluginType string, config *base.ClientAgentConfig, logger log.Logger) {
func (s *SingletonLoader) dispense(f *future, name, pluginType string, config *base.AgentConfig, logger log.Logger) {
i, err := s.loader.Dispense(name, pluginType, config, logger)
f.set(i, err)
}

View File

@@ -27,7 +27,7 @@ func TestSingleton_Dispense(t *testing.T) {
dispenseCalled := 0
s, c := harness(t)
c.DispenseF = func(_, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
c.DispenseF = func(_, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
p := &base.MockPlugin{}
i := &loader.MockInstance{
ExitedF: func() bool { return false },
@@ -77,7 +77,7 @@ func TestSingleton_Dispense_Exit_Dispense(t *testing.T) {
exited := false
dispenseCalled := 0
s, c := harness(t)
c.DispenseF = func(_, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
c.DispenseF = func(_, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
p := &base.MockPlugin{}
i := &loader.MockInstance{
ExitedF: func() bool { return exited },
@@ -125,7 +125,7 @@ func TestSingleton_DispenseError_Dispense(t *testing.T) {
require := require.New(t)
dispenseCalled := 0
good := func(_, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
good := func(_, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
p := &base.MockPlugin{}
i := &loader.MockInstance{
ExitedF: func() bool { return false },
@@ -135,7 +135,7 @@ func TestSingleton_DispenseError_Dispense(t *testing.T) {
return i, nil
}
bad := func(_, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
bad := func(_, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
dispenseCalled++
return nil, fmt.Errorf("bad")
}
@@ -169,7 +169,7 @@ func TestSingleton_ReattachError_Dispense(t *testing.T) {
dispenseCalled, reattachCalled := 0, 0
s, c := harness(t)
c.DispenseF = func(_, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
c.DispenseF = func(_, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
p := &base.MockPlugin{}
i := &loader.MockInstance{
ExitedF: func() bool { return false },
@@ -209,7 +209,7 @@ func TestSingleton_Reattach_Dispense(t *testing.T) {
dispenseCalled, reattachCalled := 0, 0
s, c := harness(t)
c.DispenseF = func(_, _ string, _ *base.ClientAgentConfig, _ log.Logger) (loader.PluginInstance, error) {
c.DispenseF = func(_, _ string, _ *base.AgentConfig, _ log.Logger) (loader.PluginInstance, error) {
dispenseCalled++
return nil, fmt.Errorf("bad")
}

View File

@@ -2,6 +2,7 @@ package version
import (
"fmt"
"reflect"
"regexp"
"strings"
)
@@ -113,6 +114,26 @@ func parseSingle(v string) (*Constraint, error) {
}, nil
}
func prereleaseCheck(v, c *Version) bool {
switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
case cPre && vPre:
// A constraint with a pre-release can only match a pre-release version
// with the same base segments.
return reflect.DeepEqual(c.Segments64(), v.Segments64())
case !cPre && vPre:
// A constraint without a pre-release can only match a version without a
// pre-release.
return false
case cPre && !vPre:
// OK, except with the pessimistic operator
case !cPre && !vPre:
// OK
}
return true
}
//-------------------------------------------------------------------
// Constraint functions
//-------------------------------------------------------------------
@@ -126,22 +147,27 @@ func constraintNotEqual(v, c *Version) bool {
}
func constraintGreaterThan(v, c *Version) bool {
return v.Compare(c) == 1
return prereleaseCheck(v, c) && v.Compare(c) == 1
}
func constraintLessThan(v, c *Version) bool {
return v.Compare(c) == -1
return prereleaseCheck(v, c) && v.Compare(c) == -1
}
func constraintGreaterThanEqual(v, c *Version) bool {
return v.Compare(c) >= 0
return prereleaseCheck(v, c) && v.Compare(c) >= 0
}
func constraintLessThanEqual(v, c *Version) bool {
return v.Compare(c) <= 0
return prereleaseCheck(v, c) && v.Compare(c) <= 0
}
func constraintPessimistic(v, c *Version) bool {
// Using a pessimistic constraint with a pre-release, restricts versions to pre-releases
if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
return false
}
// If the version being checked is naturally less than the constraint, then there
// is no way for the version to be valid against the constraint
if v.LessThan(c) {

View File

@@ -15,8 +15,8 @@ var versionRegexp *regexp.Regexp
// The raw regular expression string used for testing the validity
// of a version.
const VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
`(-?([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
`?`
// Version represents a single version.
@@ -25,6 +25,7 @@ type Version struct {
pre string
segments []int64
si int
original string
}
func init() {
@@ -59,11 +60,17 @@ func NewVersion(v string) (*Version, error) {
segments = append(segments, 0)
}
pre := matches[7]
if pre == "" {
pre = matches[4]
}
return &Version{
metadata: matches[7],
pre: matches[4],
metadata: matches[10],
pre: pre,
segments: segments,
si: si,
original: v,
}, nil
}
@@ -166,14 +173,16 @@ func comparePart(preSelf string, preOther string) int {
return 0
}
var selfInt int64
selfNumeric := true
_, err := strconv.ParseInt(preSelf, 10, 64)
selfInt, err := strconv.ParseInt(preSelf, 10, 64)
if err != nil {
selfNumeric = false
}
var otherInt int64
otherNumeric := true
_, err = strconv.ParseInt(preOther, 10, 64)
otherInt, err = strconv.ParseInt(preOther, 10, 64)
if err != nil {
otherNumeric = false
}
@@ -197,7 +206,9 @@ func comparePart(preSelf string, preOther string) int {
return -1
} else if !selfNumeric && otherNumeric {
return 1
} else if preSelf > preOther {
} else if !selfNumeric && !otherNumeric && preSelf > preOther {
return 1
} else if selfInt > otherInt {
return 1
}
@@ -297,11 +308,19 @@ func (v *Version) Segments() []int {
// for a version "1.2.3-beta", segments will return a slice of
// 1, 2, 3.
func (v *Version) Segments64() []int64 {
return v.segments
result := make([]int64, len(v.segments))
copy(result, v.segments)
return result
}
// String returns the full version string included pre-release
// and metadata information.
//
// This value is rebuilt according to the parsed segments and other
// information. Therefore, ambiguities in the version string such as
// prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and
// missing parts (1.0 => 1.0.0) will be made into a canonicalized form
// as shown in the parenthesized examples.
func (v *Version) String() string {
var buf bytes.Buffer
fmtParts := make([]string, len(v.segments))
@@ -320,3 +339,9 @@ func (v *Version) String() string {
return buf.String()
}
// Original returns the original parsed version as-is, including any
// potential whitespace, `v` prefix, etc.
func (v *Version) Original() string {
return v.original
}

2
vendor/vendor.json vendored
View File

@@ -196,7 +196,7 @@
{"path":"github.com/hashicorp/go-sockaddr/template","checksumSHA1":"PDp9DVLvf3KWxhs4G4DpIwauMSU=","revision":"6d291a969b86c4b633730bfc6b8b9d64c3aafed9","revisionTime":"2018-03-20T11:50:54Z"},
{"path":"github.com/hashicorp/go-syslog","checksumSHA1":"xZ7Ban1x//6uUIU1xtrTbCYNHBc=","revision":"42a2b573b664dbf281bd48c3cc12c086b17a39ba"},
{"path":"github.com/hashicorp/go-uuid","checksumSHA1":"mAkPa/RLuIwN53GbwIEMATexams=","revision":"64130c7a86d732268a38cb04cfbaf0cc987fda98","revisionTime":"2016-07-17T02:21:40Z"},
{"path":"github.com/hashicorp/go-version","checksumSHA1":"tUGxc7rfX0cmhOOUDhMuAZ9rWsA=","revision":"03c5bf6be031b6dd45afec16b1cf94fc8938bc77","revisionTime":"2017-02-02T08:07:59Z"},
{"path":"github.com/hashicorp/go-version","checksumSHA1":"r0pj5dMHCghpaQZ3f1BRGoKiSWw=","revision":"b5a281d3160aa11950a6182bd9a9dc2cb1e02d50","revisionTime":"2018-08-24T00:43:55Z"},
{"path":"github.com/hashicorp/golang-lru","checksumSHA1":"d9PxF1XQGLMJZRct2R8qVM/eYlE=","revision":"a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4","revisionTime":"2016-02-07T21:47:19Z"},
{"path":"github.com/hashicorp/golang-lru/simplelru","checksumSHA1":"2nOpYjx8Sn57bqlZq17yM4YJuM4=","revision":"a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4"},
{"path":"github.com/hashicorp/hcl","checksumSHA1":"8OPDk+bKyRGJoKcS4QNw9F7dpE8=","revision":"6e968a3fcdcbab092f5307fd0d85479d5af1e4dc","revisionTime":"2016-11-01T18:00:25Z"},