From d4fd73d536f150e9de8cdaec2c2df80dd3a4d088 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Fri, 14 Dec 2018 14:46:32 -0800 Subject: [PATCH 1/5] protos --- plugins/base/proto/base.pb.go | 120 ++++++++++++++++++---------------- plugins/base/proto/base.proto | 9 ++- 2 files changed, 71 insertions(+), 58 deletions(-) diff --git a/plugins/base/proto/base.pb.go b/plugins/base/proto/base.pb.go index dff5d046f..88ce71001 100644 --- a/plugins/base/proto/base.pb.go +++ b/plugins/base/proto/base.pb.go @@ -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 Nomad’s 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, } diff --git a/plugins/base/proto/base.proto b/plugins/base/proto/base.proto index 402e3b214..344e93834 100644 --- a/plugins/base/proto/base.proto +++ b/plugins/base/proto/base.proto @@ -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 Nomad’s 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 From 0cdf6634a52bf1fee6dbf366ad5c59f4c37150d7 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Fri, 14 Dec 2018 15:03:31 -0800 Subject: [PATCH 2/5] base fixes --- plugins/base/base.go | 31 ++++++++++++++++++++++--------- plugins/base/client.go | 15 ++++++++------- plugins/base/plugin_test.go | 29 ++++++++++++++++------------- plugins/base/server.go | 19 +++++++++++++------ plugins/base/testing.go | 8 ++++---- 5 files changed, 63 insertions(+), 39 deletions(-) diff --git a/plugins/base/base.go b/plugins/base/base.go index 7a9f7c5f6..f0939da79 100644 --- a/plugins/base/base.go +++ b/plugins/base/base.go @@ -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), diff --git a/plugins/base/client.go b/plugins/base/client.go index 6baf9a07d..80bc7ef4b 100644 --- a/plugins/base/client.go +++ b/plugins/base/client.go @@ -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 diff --git a/plugins/base/plugin_test.go b/plugins/base/plugin_test.go index b64ba0edf..19172f009 100644 --- a/plugins/base/plugin_test.go +++ b/plugins/base/plugin_test.go @@ -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 diff --git a/plugins/base/server.go b/plugins/base/server.go index f7ccc3fb7..af01faee4 100644 --- a/plugins/base/server.go +++ b/plugins/base/server.go @@ -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) } diff --git a/plugins/base/testing.go b/plugins/base/testing.go index bf72fe1f9..f3107a55e 100644 --- a/plugins/base/testing.go +++ b/plugins/base/testing.go @@ -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 } } From ed4f8eac6e94e27e43dc6b3b99685a0b96eb8f25 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 17 Dec 2018 16:40:58 -0800 Subject: [PATCH 3/5] Add plugin API versioning to plugin loader and plugins --- client/config/config.go | 5 +- client/devicemanager/instance.go | 4 +- client/devicemanager/manager.go | 4 +- client/devicemanager/manager_test.go | 20 +- command/agent/plugins.go | 9 +- devices/gpu/nvidia/device.go | 16 +- drivers/docker/config.go | 20 +- drivers/exec/driver.go | 14 +- drivers/java/driver.go | 14 +- drivers/lxc/driver.go | 20 +- drivers/mock/driver.go | 16 +- drivers/qemu/driver.go | 14 +- drivers/rawexec/driver.go | 20 +- drivers/rawexec/driver_test.go | 22 +- drivers/rkt/driver.go | 20 +- plugins/device/cmd/example/device.go | 15 +- plugins/device/plugin_test.go | 37 +- plugins/device/versions.go | 6 + plugins/drivers/versions.go | 6 + plugins/shared/catalog/testing.go | 9 +- plugins/shared/cmd/launcher/command/device.go | 12 +- plugins/shared/loader/api_versions.go | 15 + plugins/shared/loader/init.go | 97 ++- plugins/shared/loader/instance.go | 13 +- plugins/shared/loader/loader.go | 110 +++- plugins/shared/loader/loader_test.go | 553 ++++++++++++++---- plugins/shared/loader/plugin_test.go | 48 +- plugins/shared/loader/testing.go | 10 +- plugins/shared/singleton/singleton.go | 6 +- plugins/shared/singleton/singleton_test.go | 12 +- .../hashicorp/go-version/constraint.go | 34 +- .../hashicorp/go-version/version.go | 41 +- vendor/vendor.json | 2 +- 33 files changed, 929 insertions(+), 315 deletions(-) create mode 100644 plugins/device/versions.go create mode 100644 plugins/drivers/versions.go create mode 100644 plugins/shared/loader/api_versions.go diff --git a/client/config/config.go b/client/config/config.go index 062a453a6..13581e63b 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -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, diff --git a/client/devicemanager/instance.go b/client/devicemanager/instance.go index ab67f496f..f834062e1 100644 --- a/client/devicemanager/instance.go +++ b/client/devicemanager/instance.go @@ -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 diff --git a/client/devicemanager/manager.go b/client/devicemanager/manager.go index bf101677b..2709b2230 100644 --- a/client/devicemanager/manager.go +++ b/client/devicemanager/manager.go @@ -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 diff --git a/client/devicemanager/manager_test.go b/client/devicemanager/manager_test.go index 253c115db..c59e1fb04 100644 --- a/client/devicemanager/manager_test.go +++ b/client/devicemanager/manager_test.go @@ -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) } diff --git a/command/agent/plugins.go b/command/agent/plugins.go index 6637d69f9..0cc73a816 100644 --- a/command/agent/plugins.go +++ b/command/agent/plugins.go @@ -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 { diff --git a/devices/gpu/nvidia/device.go b/devices/gpu/nvidia/device.go index e5995eb7c..97be7d153 100644 --- a/devices/gpu/nvidia/device.go +++ b/devices/gpu/nvidia/device.go @@ -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{"v0.2.0"}, + 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 { diff --git a/drivers/docker/config.go b/drivers/docker/config.go index 5efd383e5..fa3edb3ce 100644 --- a/drivers/docker/config.go +++ b/drivers/docker/config.go @@ -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() diff --git a/drivers/exec/driver.go b/drivers/exec/driver.go index c6616f3c4..d86422d8c 100644 --- a/drivers/exec/driver.go +++ b/drivers/exec/driver.go @@ -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 } diff --git a/drivers/java/driver.go b/drivers/java/driver.go index 71ba92ccb..3575324e5 100644 --- a/drivers/java/driver.go +++ b/drivers/java/driver.go @@ -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 } diff --git a/drivers/lxc/driver.go b/drivers/lxc/driver.go index 6d51e0e35..028c356da 100644 --- a/drivers/lxc/driver.go +++ b/drivers/lxc/driver.go @@ -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 diff --git a/drivers/mock/driver.go b/drivers/mock/driver.go index 1b9abe436..1593e04fc 100644 --- a/drivers/mock/driver.go +++ b/drivers/mock/driver.go @@ -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 diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index 17ccf7f11..bcf6110a7 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -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 } diff --git a/drivers/rawexec/driver.go b/drivers/rawexec/driver.go index d839df7fe..42c0db31b 100644 --- a/drivers/rawexec/driver.go +++ b/drivers/rawexec/driver.go @@ -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 } diff --git a/drivers/rawexec/driver_test.go b/drivers/rawexec/driver_test.go index 5a7083e5e..1eb42f6dd 100644 --- a/drivers/rawexec/driver_test.go +++ b/drivers/rawexec/driver_test.go @@ -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(), diff --git a/drivers/rkt/driver.go b/drivers/rkt/driver.go index 118ee026e..643fbba87 100644 --- a/drivers/rkt/driver.go +++ b/drivers/rkt/driver.go @@ -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 } diff --git a/plugins/device/cmd/example/device.go b/plugins/device/cmd/example/device.go index 1c96ea348..475f115b0 100644 --- a/plugins/device/cmd/example/device.go +++ b/plugins/device/cmd/example/device.go @@ -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 } diff --git a/plugins/device/plugin_test.go b/plugins/device/plugin_test.go index cd8fcd8b2..a07fa329b 100644 --- a/plugins/device/plugin_test.go +++ b/plugins/device/plugin_test.go @@ -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 diff --git a/plugins/device/versions.go b/plugins/device/versions.go new file mode 100644 index 000000000..f10057f2c --- /dev/null +++ b/plugins/device/versions.go @@ -0,0 +1,6 @@ +package device + +const ( + // ApiVersion010 is the initial API version for the device plugins + ApiVersion010 = "v0.1.0" +) diff --git a/plugins/drivers/versions.go b/plugins/drivers/versions.go new file mode 100644 index 000000000..4b94d901c --- /dev/null +++ b/plugins/drivers/versions.go @@ -0,0 +1,6 @@ +package drivers + +const ( + // ApiVersion010 is the initial API version for the device plugins + ApiVersion010 = "v0.1.0" +) diff --git a/plugins/shared/catalog/testing.go b/plugins/shared/catalog/testing.go index 59bcc6cf1..19ed78bb2 100644 --- a/plugins/shared/catalog/testing.go +++ b/plugins/shared/catalog/testing.go @@ -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 { diff --git a/plugins/shared/cmd/launcher/command/device.go b/plugins/shared/cmd/launcher/command/device.go index aadc215eb..01855da7b 100644 --- a/plugins/shared/cmd/launcher/command/device.go +++ b/plugins/shared/cmd/launcher/command/device.go @@ -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 } diff --git a/plugins/shared/loader/api_versions.go b/plugins/shared/loader/api_versions.go new file mode 100644 index 000000000..8ceb02da0 --- /dev/null +++ b/plugins/shared/loader/api_versions.go @@ -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: []string{device.ApiVersion010}, + base.PluginTypeDriver: []string{device.ApiVersion010}, + } +) diff --git a/plugins/shared/loader/init.go b/plugins/shared/loader/init.go index 9013fb13d..89f09198c 100644 --- a/plugins/shared/loader/init.go +++ b/plugins/shared/loader/init.go @@ -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 diff --git a/plugins/shared/loader/instance.go b/plugins/shared/loader/instance.go index bb074e79f..d20bff4a2 100644 --- a/plugins/shared/loader/instance.go +++ b/plugins/shared/loader/instance.go @@ -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 diff --git a/plugins/shared/loader/loader.go b/plugins/shared/loader/loader.go index d2175af07..b07904832 100644 --- a/plugins/shared/loader/loader.go +++ b/plugins/shared/loader/loader.go @@ -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 } diff --git a/plugins/shared/loader/loader_test.go b/plugins/shared/loader/loader_test.go index 71363ad45..82c8bb0b2 100644 --- a/plugins/shared/loader/loader_test.go +++ b/plugins/shared/loader/loader_test.go @@ -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: []string{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: []string{"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: []string{"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]) + } + } + }) + } +} diff --git a/plugins/shared/loader/plugin_test.go b/plugins/shared/loader/plugin_test.go index ea9b129fd..d619dd249 100644 --- a/plugins/shared/loader/plugin_test.go +++ b/plugins/shared/loader/plugin_test.go @@ -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 } diff --git a/plugins/shared/loader/testing.go b/plugins/shared/loader/testing.go index 501e33166..f811804ec 100644 --- a/plugins/shared/loader/testing.go +++ b/plugins/shared/loader/testing.go @@ -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 }, } } diff --git a/plugins/shared/singleton/singleton.go b/plugins/shared/singleton/singleton.go index 100e69070..260b092c5 100644 --- a/plugins/shared/singleton/singleton.go +++ b/plugins/shared/singleton/singleton.go @@ -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) } diff --git a/plugins/shared/singleton/singleton_test.go b/plugins/shared/singleton/singleton_test.go index 3151f8a84..e1f122513 100644 --- a/plugins/shared/singleton/singleton_test.go +++ b/plugins/shared/singleton/singleton_test.go @@ -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") } diff --git a/vendor/github.com/hashicorp/go-version/constraint.go b/vendor/github.com/hashicorp/go-version/constraint.go index 8c73df060..d05575961 100644 --- a/vendor/github.com/hashicorp/go-version/constraint.go +++ b/vendor/github.com/hashicorp/go-version/constraint.go @@ -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) { diff --git a/vendor/github.com/hashicorp/go-version/version.go b/vendor/github.com/hashicorp/go-version/version.go index dfe509caa..4d1e6e221 100644 --- a/vendor/github.com/hashicorp/go-version/version.go +++ b/vendor/github.com/hashicorp/go-version/version.go @@ -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 +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 0b07d500f..2a27be25d 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -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"}, From 07a7555acdcae7076c279f04b827d782315519f0 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Tue, 18 Dec 2018 15:07:30 -0800 Subject: [PATCH 4/5] lint --- client/fingerprint_manager_test.go | 2 +- devices/gpu/nvidia/device.go | 2 +- drivers/rkt/driver_test.go | 12 ++++++++---- plugins/shared/loader/api_versions.go | 4 ++-- plugins/shared/loader/loader_test.go | 6 +++--- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/client/fingerprint_manager_test.go b/client/fingerprint_manager_test.go index 922559ba2..f9095a0b2 100644 --- a/client/fingerprint_manager_test.go +++ b/client/fingerprint_manager_test.go @@ -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++ } diff --git a/devices/gpu/nvidia/device.go b/devices/gpu/nvidia/device.go index 97be7d153..110287681 100644 --- a/devices/gpu/nvidia/device.go +++ b/devices/gpu/nvidia/device.go @@ -38,7 +38,7 @@ var ( // pluginInfo describes the plugin pluginInfo = &base.PluginInfoResponse{ Type: base.PluginTypeDevice, - PluginApiVersions: []string{"v0.2.0"}, + PluginApiVersions: []string{device.ApiVersion010}, PluginVersion: "0.1.0", Name: pluginName, } diff --git a/drivers/rkt/driver_test.go b/drivers/rkt/driver_test.go index 9ac402dd7..95a75e371 100644 --- a/drivers/rkt/driver_test.go +++ b/drivers/rkt/driver_test.go @@ -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) diff --git a/plugins/shared/loader/api_versions.go b/plugins/shared/loader/api_versions.go index 8ceb02da0..51a680b9c 100644 --- a/plugins/shared/loader/api_versions.go +++ b/plugins/shared/loader/api_versions.go @@ -9,7 +9,7 @@ var ( // AgentSupportedApiVersions is the set of API versions supported by the // Nomad agent by plugin type. AgentSupportedApiVersions = map[string][]string{ - base.PluginTypeDevice: []string{device.ApiVersion010}, - base.PluginTypeDriver: []string{device.ApiVersion010}, + base.PluginTypeDevice: {device.ApiVersion010}, + base.PluginTypeDriver: {device.ApiVersion010}, } ) diff --git a/plugins/shared/loader/loader_test.go b/plugins/shared/loader/loader_test.go index 82c8bb0b2..f8b31aea8 100644 --- a/plugins/shared/loader/loader_test.go +++ b/plugins/shared/loader/loader_test.go @@ -22,7 +22,7 @@ var ( // supportedApiVersions is the set of api versions that the "client" can // support supportedApiVersions = map[string][]string{ - base.PluginTypeDevice: []string{device.ApiVersion010}, + base.PluginTypeDevice: {device.ApiVersion010}, } ) @@ -177,7 +177,7 @@ func TestPluginLoader_External_ApiVersions(t *testing.T) { Logger: logger, PluginDir: h.pluginDir(), SupportedVersions: map[string][]string{ - base.PluginTypeDevice: []string{"0.2.0", "0.2.1", "0.3.0"}, + base.PluginTypeDevice: {"0.2.0", "0.2.1", "0.3.0"}, }, Configs: []*config.PluginConfig{ { @@ -527,7 +527,7 @@ func TestPluginLoader_Internal_ApiVersions(t *testing.T) { Logger: logger, PluginDir: h.pluginDir(), SupportedVersions: map[string][]string{ - base.PluginTypeDevice: []string{"0.2.0", "0.2.1", "0.3.0"}, + base.PluginTypeDevice: {"0.2.0", "0.2.1", "0.3.0"}, }, InternalPlugins: map[PluginID]*InternalPluginConfig{ { From 52202c68fc2e5edc44a69c1630dd28f60389d24d Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Tue, 18 Dec 2018 15:21:10 -0800 Subject: [PATCH 5/5] fix docker launching plugins --- drivers/docker/driver_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/docker/driver_test.go b/drivers/docker/driver_test.go index 8335ff97d..25fd921a6 100644 --- a/drivers/docker/driver_test.go +++ b/drivers/docker/driver_test.go @@ -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,