From efe042779e7d87d9765cbd6a6799449197cd5cc2 Mon Sep 17 00:00:00 2001 From: Cory Snider Date: Fri, 23 Feb 2024 16:29:38 -0500 Subject: [PATCH] agent,manager,node: define our own plugin ifaces Allow Moby plugins to be used without Swarmkit having to import github.com/docker/docker packages by defining our own interfaces which cover the subset of functionality we use. Adapters will have to be written on the Moby side as their API contains methods which return concrete struct types, but they only need to be trivial wrappers as their *plugins.Client satisfies our plugin.Client interface. Signed-off-by: Cory Snider --- agent/csi/plugin/manager.go | 15 ++--- agent/csi/plugin/manager_test.go | 8 +-- agent/csi/plugin/plugin.go | 12 ++-- agent/csi/plugin/plugin_fake_test.go | 5 +- agent/csi/plugin/plugin_test.go | 4 +- agent/csi/volumes.go | 5 +- agent/dependency.go | 5 +- agent/worker_test.go | 6 +- manager/csi/fakes_test.go | 8 +-- manager/csi/manager.go | 14 ++-- manager/csi/manager_test.go | 18 +++--- manager/csi/plugin.go | 8 +-- manager/dispatcher/dispatcher_test.go | 93 ++++++++++++++------------- manager/drivers/provider.go | 8 +-- manager/drivers/secrets.go | 6 +- manager/manager.go | 4 +- node/node.go | 4 +- node/plugin/pluginapi.go | 23 +++++++ testutils/fake_plugingetter.go | 48 ++++---------- 19 files changed, 147 insertions(+), 147 deletions(-) create mode 100644 node/plugin/pluginapi.go diff --git a/agent/csi/plugin/manager.go b/agent/csi/plugin/manager.go index 26c74f79f5..b4fc047263 100644 --- a/agent/csi/plugin/manager.go +++ b/agent/csi/plugin/manager.go @@ -5,9 +5,8 @@ import ( "fmt" "sync" - "github.com/docker/docker/pkg/plugingetter" - "github.com/moby/swarmkit/v2/api" + "github.com/moby/swarmkit/v2/node/plugin" ) const ( @@ -35,15 +34,15 @@ type pluginManager struct { // newNodePluginFunc usually points to NewNodePlugin. However, for testing, // NewNodePlugin can be swapped out with a function that creates fake node // plugins - newNodePluginFunc func(string, plugingetter.CompatPlugin, plugingetter.PluginAddr, SecretGetter) NodePlugin + newNodePluginFunc func(string, plugin.AddrPlugin, SecretGetter) NodePlugin // secrets is a SecretGetter for use by node plugins. secrets SecretGetter - pg plugingetter.PluginGetter + pg plugin.Getter } -func NewManager(pg plugingetter.PluginGetter, secrets SecretGetter) Manager { +func NewManager(pg plugin.Getter, secrets SecretGetter) Manager { return &pluginManager{ plugins: map[string]NodePlugin{}, newNodePluginFunc: NewNodePlugin, @@ -104,17 +103,17 @@ func (pm *pluginManager) getPlugin(name string) (NodePlugin, error) { return p, nil } - pc, err := pm.pg.Get(name, DockerCSIPluginCap, plugingetter.Lookup) + pc, err := pm.pg.Get(name, DockerCSIPluginCap) if err != nil { return nil, err } - pa, ok := pc.(plugingetter.PluginAddr) + pa, ok := pc.(plugin.AddrPlugin) if !ok { return nil, fmt.Errorf("plugin does not implement PluginAddr interface") } - p := pm.newNodePluginFunc(name, pc, pa, pm.secrets) + p := pm.newNodePluginFunc(name, pa, pm.secrets) pm.plugins[name] = p return p, nil } diff --git a/agent/csi/plugin/manager_test.go b/agent/csi/plugin/manager_test.go index 092efa43f4..f597c68831 100644 --- a/agent/csi/plugin/manager_test.go +++ b/agent/csi/plugin/manager_test.go @@ -18,7 +18,7 @@ var _ = Describe("Manager", func() { BeforeEach(func() { pg = &testutils.FakePluginGetter{ - Plugins: map[string]*testutils.FakeCompatPlugin{}, + Plugins: map[string]*testutils.FakePlugin{}, } pm = &pluginManager{ @@ -27,21 +27,21 @@ var _ = Describe("Manager", func() { pg: pg, } - pg.Plugins["plug1"] = &testutils.FakeCompatPlugin{ + pg.Plugins["plug1"] = &testutils.FakePlugin{ PluginName: "plug1", PluginAddr: &net.UnixAddr{ Net: "unix", Name: "", }, } - pg.Plugins["plug2"] = &testutils.FakeCompatPlugin{ + pg.Plugins["plug2"] = &testutils.FakePlugin{ PluginName: "plug2", PluginAddr: &net.UnixAddr{ Net: "unix", Name: "fail", }, } - pg.Plugins["plug3"] = &testutils.FakeCompatPlugin{ + pg.Plugins["plug3"] = &testutils.FakePlugin{ PluginName: "plug3", PluginAddr: &net.UnixAddr{ Net: "unix", diff --git a/agent/csi/plugin/plugin.go b/agent/csi/plugin/plugin.go index 560474d025..a721ac25a8 100644 --- a/agent/csi/plugin/plugin.go +++ b/agent/csi/plugin/plugin.go @@ -11,10 +11,10 @@ import ( "google.golang.org/grpc/status" "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/docker/docker/pkg/plugingetter" "github.com/moby/swarmkit/v2/api" "github.com/moby/swarmkit/v2/internal/csi/capability" "github.com/moby/swarmkit/v2/log" + "github.com/moby/swarmkit/v2/node/plugin" ) // SecretGetter is a reimplementation of the exec.SecretGetter interface in the @@ -88,17 +88,17 @@ const ( TargetPublishPath string = "/data/published" ) -func NewNodePlugin(name string, pc plugingetter.CompatPlugin, pa plugingetter.PluginAddr, secrets SecretGetter) NodePlugin { - return newNodePlugin(name, pc, pa, secrets) +func NewNodePlugin(name string, p plugin.AddrPlugin, secrets SecretGetter) NodePlugin { + return newNodePlugin(name, p, secrets) } // newNodePlugin returns a raw nodePlugin object, not behind an interface. this // is useful for testing. -func newNodePlugin(name string, pc plugingetter.CompatPlugin, pa plugingetter.PluginAddr, secrets SecretGetter) *nodePlugin { +func newNodePlugin(name string, p plugin.AddrPlugin, secrets SecretGetter) *nodePlugin { return &nodePlugin{ name: name, - socket: fmt.Sprintf("%s://%s", pa.Addr().Network(), pa.Addr().String()), - scopePath: pc.ScopedPath, + socket: fmt.Sprintf("%s://%s", p.Addr().Network(), p.Addr().String()), + scopePath: p.ScopedPath, secrets: secrets, volumeMap: map[string]*volumePublishStatus{}, } diff --git a/agent/csi/plugin/plugin_fake_test.go b/agent/csi/plugin/plugin_fake_test.go index f1fdc60c5c..16e22f1276 100644 --- a/agent/csi/plugin/plugin_fake_test.go +++ b/agent/csi/plugin/plugin_fake_test.go @@ -4,9 +4,8 @@ import ( "context" "fmt" - "github.com/docker/docker/pkg/plugingetter" - "github.com/moby/swarmkit/v2/api" + mobyplugin "github.com/moby/swarmkit/v2/node/plugin" ) // plugin_fake_test.go contains code for faking node plugins in the context of @@ -20,7 +19,7 @@ type fakeNodePlugin struct { // newFakeNodePlugin has the same signature as NewNodePlugin, allowing it to be // substituted in testing. -func newFakeNodePlugin(name string, pc plugingetter.CompatPlugin, pa plugingetter.PluginAddr, secrets SecretGetter) NodePlugin { +func newFakeNodePlugin(name string, pa mobyplugin.AddrPlugin, secrets SecretGetter) NodePlugin { return &fakeNodePlugin{ name: name, socket: pa.Addr().String(), diff --git a/agent/csi/plugin/plugin_test.go b/agent/csi/plugin/plugin_test.go index 5427a45fbb..0e312c5080 100644 --- a/agent/csi/plugin/plugin_test.go +++ b/agent/csi/plugin/plugin_test.go @@ -15,11 +15,11 @@ import ( ) func newVolumeClient(name string, nodeID string) *nodePlugin { - p := &testutils.FakeCompatPlugin{ + p := &testutils.FakePlugin{ PluginName: name, PluginAddr: &net.UnixAddr{}, } - n := newNodePlugin(name, p, p, nil) + n := newNodePlugin(name, p, nil) n.staging = true fakeNodeClient := newFakeNodeClient(true, nodeID) diff --git a/agent/csi/volumes.go b/agent/csi/volumes.go index ae276d5d29..97539286df 100644 --- a/agent/csi/volumes.go +++ b/agent/csi/volumes.go @@ -6,12 +6,11 @@ import ( "sync" "time" - "github.com/docker/docker/pkg/plugingetter" - "github.com/moby/swarmkit/v2/agent/csi/plugin" "github.com/moby/swarmkit/v2/agent/exec" "github.com/moby/swarmkit/v2/api" "github.com/moby/swarmkit/v2/log" + mobyplugin "github.com/moby/swarmkit/v2/node/plugin" "github.com/moby/swarmkit/v2/volumequeue" ) @@ -46,7 +45,7 @@ type volumes struct { } // NewManager returns a place to store volumes. -func NewManager(pg plugingetter.PluginGetter, secrets exec.SecretGetter) exec.VolumesManager { +func NewManager(pg mobyplugin.Getter, secrets exec.SecretGetter) exec.VolumesManager { r := &volumes{ volumes: map[string]volumeState{}, plugins: plugin.NewManager(pg, secrets), diff --git a/agent/dependency.go b/agent/dependency.go index d123e30a1b..16aacf8d72 100644 --- a/agent/dependency.go +++ b/agent/dependency.go @@ -1,13 +1,12 @@ package agent import ( - "github.com/docker/docker/pkg/plugingetter" - "github.com/moby/swarmkit/v2/agent/configs" "github.com/moby/swarmkit/v2/agent/csi" "github.com/moby/swarmkit/v2/agent/exec" "github.com/moby/swarmkit/v2/agent/secrets" "github.com/moby/swarmkit/v2/api" + "github.com/moby/swarmkit/v2/node/plugin" ) type dependencyManager struct { @@ -18,7 +17,7 @@ type dependencyManager struct { // NewDependencyManager creates a dependency manager object that wraps // objects which provide access to various dependency types. -func NewDependencyManager(pg plugingetter.PluginGetter) exec.DependencyManager { +func NewDependencyManager(pg plugin.Getter) exec.DependencyManager { d := &dependencyManager{ secrets: secrets.NewManager(), configs: configs.NewManager(), diff --git a/agent/worker_test.go b/agent/worker_test.go index 03cf3b2712..40de4adc75 100644 --- a/agent/worker_test.go +++ b/agent/worker_test.go @@ -42,7 +42,7 @@ func TestWorkerAssign(t *testing.T) { defer cleanup() pg := &testutils.FakePluginGetter{ - Plugins: map[string]*testutils.FakeCompatPlugin{ + Plugins: map[string]*testutils.FakePlugin{ "plugin-1": { PluginName: "plugin-1", PluginAddr: &net.UnixAddr{}, @@ -268,7 +268,7 @@ func TestWorkerWait(t *testing.T) { ctx := context.Background() pg := &testutils.FakePluginGetter{ - Plugins: map[string]*testutils.FakeCompatPlugin{ + Plugins: map[string]*testutils.FakePlugin{ "plugin-1": { PluginName: "plugin-1", PluginAddr: &net.UnixAddr{}, @@ -416,7 +416,7 @@ func TestWorkerUpdate(t *testing.T) { ctx := context.Background() pg := &testutils.FakePluginGetter{ - Plugins: map[string]*testutils.FakeCompatPlugin{ + Plugins: map[string]*testutils.FakePlugin{ "plugin-1": { PluginName: "plugin-1", PluginAddr: &net.UnixAddr{}, diff --git a/manager/csi/fakes_test.go b/manager/csi/fakes_test.go index 254c8ba0c4..4e434f27ca 100644 --- a/manager/csi/fakes_test.go +++ b/manager/csi/fakes_test.go @@ -10,8 +10,8 @@ import ( "github.com/golang/protobuf/ptypes/wrappers" "google.golang.org/grpc" - "github.com/docker/docker/pkg/plugingetter" "github.com/moby/swarmkit/v2/api" + mobyplugin "github.com/moby/swarmkit/v2/node/plugin" ) const ( @@ -203,11 +203,11 @@ type fakePluginMaker struct { plugins map[string]*fakePlugin } -func (fpm *fakePluginMaker) newFakePlugin(pc plugingetter.CompatPlugin, pa plugingetter.PluginAddr, provider SecretProvider) Plugin { +func (fpm *fakePluginMaker) newFakePlugin(pa mobyplugin.AddrPlugin, provider SecretProvider) Plugin { fpm.Lock() defer fpm.Unlock() p := &fakePlugin{ - name: pc.Name(), + name: pa.Name(), socket: pa.Addr().String(), swarmToCSI: map[string]string{}, volumesCreated: map[string]*api.Volume{}, @@ -216,7 +216,7 @@ func (fpm *fakePluginMaker) newFakePlugin(pc plugingetter.CompatPlugin, pa plugi volumesUnpublished: map[string][]string{}, removedIDs: map[string]struct{}{}, } - fpm.plugins[pc.Name()] = p + fpm.plugins[pa.Name()] = p return p } diff --git a/manager/csi/manager.go b/manager/csi/manager.go index adb2bf2611..358079b6f2 100644 --- a/manager/csi/manager.go +++ b/manager/csi/manager.go @@ -7,12 +7,12 @@ import ( "sync" "time" - "github.com/docker/docker/pkg/plugingetter" "github.com/docker/go-events" "github.com/moby/swarmkit/v2/api" "github.com/moby/swarmkit/v2/log" "github.com/moby/swarmkit/v2/manager/state/store" + mobyplugin "github.com/moby/swarmkit/v2/node/plugin" "github.com/moby/swarmkit/v2/volumequeue" ) @@ -36,12 +36,12 @@ type Manager struct { // pg is the plugingetter, which allows us to access the Docker Engine's // plugin store. - pg plugingetter.PluginGetter + pg mobyplugin.Getter // newPlugin is a function which returns an object implementing the Plugin // interface. It allows us to swap out the implementation of plugins while // unit-testing the Manager - newPlugin func(pc plugingetter.CompatPlugin, pa plugingetter.PluginAddr, provider SecretProvider) Plugin + newPlugin func(p mobyplugin.AddrPlugin, provider SecretProvider) Plugin // synchronization for starting and stopping the Manager startOnce sync.Once @@ -55,7 +55,7 @@ type Manager struct { pendingVolumes *volumequeue.VolumeQueue } -func NewManager(s *store.MemoryStore, pg plugingetter.PluginGetter) *Manager { +func NewManager(s *store.MemoryStore, pg mobyplugin.Getter) *Manager { return &Manager{ store: s, stopChan: make(chan struct{}), @@ -469,7 +469,7 @@ func (vm *Manager) getPlugin(name string) (Plugin, error) { } // otherwise, we need to load the plugin. - pc, err := vm.pg.Get(name, DockerCSIPluginCap, plugingetter.Lookup) + pc, err := vm.pg.Get(name, DockerCSIPluginCap) if err != nil { return nil, err } @@ -478,12 +478,12 @@ func (vm *Manager) getPlugin(name string) (Plugin, error) { return nil, errors.New("driver \"" + name + "\" not found") } - pa, ok := pc.(plugingetter.PluginAddr) + pa, ok := pc.(mobyplugin.AddrPlugin) if !ok { return nil, errors.New("plugin for driver \"" + name + "\" does not implement PluginAddr") } - p := vm.newPlugin(pc, pa, vm.provider) + p := vm.newPlugin(pa, vm.provider) vm.plugins[name] = p return p, nil diff --git a/manager/csi/manager_test.go b/manager/csi/manager_test.go index 229d6ad63a..6cd2b73e3e 100644 --- a/manager/csi/manager_test.go +++ b/manager/csi/manager_test.go @@ -63,7 +63,7 @@ var _ = Describe("Manager", func() { plugins: map[string]*fakePlugin{}, } pluginGetter = &testutils.FakePluginGetter{ - Plugins: map[string]*testutils.FakeCompatPlugin{}, + Plugins: map[string]*testutils.FakePlugin{}, } s = store.NewMemoryStore(nil) @@ -108,14 +108,14 @@ var _ = Describe("Manager", func() { When("starting up", func() { BeforeEach(func() { - pluginGetter.Plugins["newPlugin"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["newPlugin"] = &testutils.FakePlugin{ PluginName: "newPlugin", PluginAddr: &net.UnixAddr{ Net: "unix", Name: "unix:///whatever.sock", }, } - pluginGetter.Plugins["differentPlugin"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["differentPlugin"] = &testutils.FakePlugin{ PluginName: "differentPlugin", PluginAddr: &net.UnixAddr{ Net: "unix", @@ -233,14 +233,14 @@ var _ = Describe("Manager", func() { When("a volume is created", func() { BeforeEach(func() { - pluginGetter.Plugins["somePlugin"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["somePlugin"] = &testutils.FakePlugin{ PluginName: "somePlugin", PluginAddr: &net.UnixAddr{ Net: "unix", Name: "unix:///whatever.sock", }, } - pluginGetter.Plugins["someOtherPlugin"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["someOtherPlugin"] = &testutils.FakePlugin{ PluginName: "someOtherPlugin", PluginAddr: &net.UnixAddr{ Net: "unix", @@ -297,14 +297,14 @@ var _ = Describe("Manager", func() { Describe("managing node inventory", func() { BeforeEach(func() { - pluginGetter.Plugins["newPlugin"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["newPlugin"] = &testutils.FakePlugin{ PluginName: "newPlugin", PluginAddr: &net.UnixAddr{ Net: "unix", Name: "unix:///whatever.sock", }, } - pluginGetter.Plugins["differentPlugin"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["differentPlugin"] = &testutils.FakePlugin{ PluginName: "differentPlugin", PluginAddr: &net.UnixAddr{ Net: "unix", @@ -469,7 +469,7 @@ var _ = Describe("Manager", func() { v1 *api.Volume ) BeforeEach(func() { - pluginGetter.Plugins["plug1"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["plug1"] = &testutils.FakePlugin{ PluginName: "plug1", PluginAddr: &net.UnixAddr{ Net: "unix", @@ -666,7 +666,7 @@ var _ = Describe("Manager", func() { Describe("removing a Volume", func() { BeforeEach(func() { - pluginGetter.Plugins["plug"] = &testutils.FakeCompatPlugin{ + pluginGetter.Plugins["plug"] = &testutils.FakePlugin{ PluginName: "plug", PluginAddr: &net.UnixAddr{ Net: "unix", diff --git a/manager/csi/plugin.go b/manager/csi/plugin.go index 6a1cd70a2e..77a1f0f4c9 100644 --- a/manager/csi/plugin.go +++ b/manager/csi/plugin.go @@ -10,10 +10,10 @@ import ( "google.golang.org/grpc/status" "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/docker/docker/pkg/plugingetter" "github.com/moby/swarmkit/v2/api" "github.com/moby/swarmkit/v2/internal/csi/capability" "github.com/moby/swarmkit/v2/log" + mobyplugin "github.com/moby/swarmkit/v2/node/plugin" ) // Plugin is the interface for a CSI controller plugin. @@ -74,12 +74,12 @@ type plugin struct { // the same object. By taking both parts here, we can push off the work of // assuring that the given plugin implements the PluginAddr interface without // having to typecast in this constructor. -func NewPlugin(pc plugingetter.CompatPlugin, pa plugingetter.PluginAddr, provider SecretProvider) Plugin { +func NewPlugin(p mobyplugin.AddrPlugin, provider SecretProvider) Plugin { return &plugin{ - name: pc.Name(), + name: p.Name(), // TODO(dperny): verify that we do not need to include the Network() // portion of the Addr. - socket: fmt.Sprintf("%s://%s", pa.Addr().Network(), pa.Addr().String()), + socket: fmt.Sprintf("%s://%s", p.Addr().Network(), p.Addr().String()), provider: provider, swarmToCSI: map[string]string{}, csiToSwarm: map[string]string{}, diff --git a/manager/dispatcher/dispatcher_test.go b/manager/dispatcher/dispatcher_test.go index 2d5e7e5d84..b5e4311e07 100644 --- a/manager/dispatcher/dispatcher_test.go +++ b/manager/dispatcher/dispatcher_test.go @@ -6,10 +6,7 @@ import ( "encoding/json" "errors" "fmt" - "io" "net" - "net/http" - "net/http/httptest" "sync" "testing" "time" @@ -18,8 +15,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" - "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/pkg/plugins" "github.com/docker/go-events" "github.com/moby/swarmkit/v2/api" "github.com/moby/swarmkit/v2/ca" @@ -27,6 +22,7 @@ import ( "github.com/moby/swarmkit/v2/identity" "github.com/moby/swarmkit/v2/manager/drivers" "github.com/moby/swarmkit/v2/manager/state/store" + "github.com/moby/swarmkit/v2/node/plugin" "github.com/moby/swarmkit/v2/testutils" digest "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" @@ -52,7 +48,6 @@ func (gd *grpcDispatcher) Close() { } gd.dispatcherServer.Stop() gd.grpcServer.Stop() - gd.PluginGetter.Close() gd.testCA.Stop() } @@ -423,12 +418,9 @@ func TestAssignmentsSecretDriver(t *testing.T) { errSecretName: {Err: "Error from driver"}, } - mux := http.NewServeMux() - mux.HandleFunc(drivers.SecretsProviderAPI, func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - body, err := io.ReadAll(r.Body) + var mux MockPluginClient + mux.HandleFunc(drivers.SecretsProviderAPI, func(body []byte) (interface{}, error) { var request drivers.SecretsProviderRequest - assert.NoError(t, err) assert.NoError(t, json.Unmarshal(body, &request)) response := responses[request.SecretName] assert.Equal(t, serviceName, request.ServiceName) @@ -438,14 +430,12 @@ func TestAssignmentsSecretDriver(t *testing.T) { assert.EqualValues(t, portConfig, request.ServiceEndpointSpec.Ports[0]) assert.EqualValues(t, serviceLabels, request.ServiceLabels) assert.NotNil(t, response) - resp, err := json.Marshal(response) - assert.NoError(t, err) - w.Write(resp) + return response, nil }) gd := startDispatcher(t, DefaultConfig()) defer gd.Close() - assert.NoError(t, gd.PluginGetter.SetupPlugin(secretDriver, mux)) + assert.NoError(t, gd.PluginGetter.SetupPlugin(secretDriver, &mux)) expectedSessionID, nodeID := getSessionAndNodeID(t, gd.Clients[0]) @@ -2333,59 +2323,41 @@ func TestClusterUpdatesSendMessages(t *testing.T) { // mockPluginGetter enables mocking the server plugin getter with customized plugins type mockPluginGetter struct { - addr string - server *httptest.Server name string - plugin plugingetter.CompatPlugin + plugin plugin.Plugin } +var _ plugin.Getter = &mockPluginGetter{} + // SetupPlugin setup a new plugin - the same plugin wil always return in all calls -func (m *mockPluginGetter) SetupPlugin(name string, handler http.Handler) error { - m.server = httptest.NewServer(handler) - client, err := plugins.NewClient(m.server.URL, nil) - if err != nil { - return err - } +func (m *mockPluginGetter) SetupPlugin(name string, client plugin.Client) error { m.plugin = NewMockPlugin(m.name, client) m.name = name return nil } -// Close closes the mock plugin getter -func (m *mockPluginGetter) Close() { - if m.server == nil { - return - } - m.server.Close() -} - -func (m *mockPluginGetter) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) { +func (m *mockPluginGetter) Get(name, capability string) (plugin.Plugin, error) { if name != m.name { return nil, fmt.Errorf("plugin with name %s not defined", name) } return m.plugin, nil } -func (m *mockPluginGetter) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) { - return nil, nil -} -func (m *mockPluginGetter) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin { +func (m *mockPluginGetter) GetAllManagedPluginsByCap(capability string) []plugin.Plugin { return nil } -func (m *mockPluginGetter) Handle(capability string, callback func(string, *plugins.Client)) { -} // MockPlugin mocks a v2 docker plugin type MockPlugin struct { - client *plugins.Client + client plugin.Client name string } // NewMockPlugin creates a new v2 plugin fake (returns the specified client and name for all calls) -func NewMockPlugin(name string, client *plugins.Client) *MockPlugin { +func NewMockPlugin(name string, client plugin.Client) *MockPlugin { return &MockPlugin{name: name, client: client} } -func (m *MockPlugin) Client() *plugins.Client { +func (m *MockPlugin) Client() plugin.Client { return m.client } func (m *MockPlugin) Name() string { @@ -2394,10 +2366,39 @@ func (m *MockPlugin) Name() string { func (m *MockPlugin) ScopedPath(_ string) string { return "" } -func (m *MockPlugin) BasePath() string { - return "" +type MockPluginHandlerFn func(argsJSON []byte) (interface{}, error) + +type MockPluginClient struct { + handlers map[string]MockPluginHandlerFn } -func (m *MockPlugin) IsV1() bool { - return false + +func (mc *MockPluginClient) HandleFunc(method string, fn MockPluginHandlerFn) { + if mc.handlers == nil { + mc.handlers = make(map[string]MockPluginHandlerFn) + } + if _, ok := mc.handlers[method]; ok { + panic(fmt.Sprintf("handler for %s already exists", method)) + } + mc.handlers[method] = fn +} + +func (mc *MockPluginClient) Call(method string, args, ret interface{}) error { + fn, ok := mc.handlers[method] + if !ok { + return fmt.Errorf("no handler for %s", method) + } + jsonArgs, err := json.Marshal(args) + if err != nil { + return err + } + res, err := fn(jsonArgs) + if err != nil { + return err + } + jsonRes, err := json.Marshal(res) + if err != nil { + return fmt.Errorf("error marshalling response: %v", err) + } + return json.Unmarshal(jsonRes, ret) } diff --git a/manager/drivers/provider.go b/manager/drivers/provider.go index 899c54e1a7..1dd3fa620b 100644 --- a/manager/drivers/provider.go +++ b/manager/drivers/provider.go @@ -3,17 +3,17 @@ package drivers import ( "fmt" - "github.com/docker/docker/pkg/plugingetter" "github.com/moby/swarmkit/v2/api" + "github.com/moby/swarmkit/v2/node/plugin" ) // DriverProvider provides external drivers type DriverProvider struct { - pluginGetter plugingetter.PluginGetter + pluginGetter plugin.Getter } // New returns a new driver provider -func New(pluginGetter plugingetter.PluginGetter) *DriverProvider { +func New(pluginGetter plugin.Getter) *DriverProvider { return &DriverProvider{pluginGetter: pluginGetter} } @@ -26,7 +26,7 @@ func (m *DriverProvider) NewSecretDriver(driver *api.Driver) (*SecretDriver, err return nil, fmt.Errorf("driver specification is nil") } // Search for the specified plugin - plugin, err := m.pluginGetter.Get(driver.Name, SecretsProviderCapability, plugingetter.Lookup) + plugin, err := m.pluginGetter.Get(driver.Name, SecretsProviderCapability) if err != nil { return nil, err } diff --git a/manager/drivers/secrets.go b/manager/drivers/secrets.go index ec221a4cb6..63d6e47c23 100644 --- a/manager/drivers/secrets.go +++ b/manager/drivers/secrets.go @@ -3,9 +3,9 @@ package drivers import ( "fmt" - "github.com/docker/docker/pkg/plugingetter" "github.com/moby/swarmkit/v2/api" "github.com/moby/swarmkit/v2/api/naming" + "github.com/moby/swarmkit/v2/node/plugin" ) const ( @@ -18,11 +18,11 @@ const ( // SecretDriver provides secrets from different stores type SecretDriver struct { - plugin plugingetter.CompatPlugin + plugin plugin.Plugin } // NewSecretDriver creates a new driver that provides third party secrets -func NewSecretDriver(plugin plugingetter.CompatPlugin) *SecretDriver { +func NewSecretDriver(plugin plugin.Plugin) *SecretDriver { return &SecretDriver{plugin: plugin} } diff --git a/manager/manager.go b/manager/manager.go index 06e1971c21..8e00c57477 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -13,7 +13,6 @@ import ( "syscall" "time" - "github.com/docker/docker/pkg/plugingetter" "github.com/docker/go-events" gmetrics "github.com/docker/go-metrics" gogotypes "github.com/gogo/protobuf/types" @@ -45,6 +44,7 @@ import ( "github.com/moby/swarmkit/v2/manager/state/raft/transport" "github.com/moby/swarmkit/v2/manager/state/store" "github.com/moby/swarmkit/v2/manager/watchapi" + "github.com/moby/swarmkit/v2/node/plugin" "github.com/moby/swarmkit/v2/remotes" "github.com/moby/swarmkit/v2/xnet" "github.com/pkg/errors" @@ -123,7 +123,7 @@ type Config struct { Availability api.NodeSpec_Availability // PluginGetter provides access to docker's plugin inventory. - PluginGetter plugingetter.PluginGetter + PluginGetter plugin.Getter // FIPS is a boolean stating whether the node is FIPS enabled - if this is the // first node in the cluster, this setting is used to set the cluster-wide mandatory diff --git a/node/node.go b/node/node.go index b6b6ec3711..eda5266a73 100644 --- a/node/node.go +++ b/node/node.go @@ -15,7 +15,6 @@ import ( "sync" "time" - "github.com/docker/docker/pkg/plugingetter" "github.com/docker/go-metrics" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/moby/swarmkit/v2/agent" @@ -30,6 +29,7 @@ import ( "github.com/moby/swarmkit/v2/manager" "github.com/moby/swarmkit/v2/manager/allocator/networkallocator" "github.com/moby/swarmkit/v2/manager/encryption" + "github.com/moby/swarmkit/v2/node/plugin" "github.com/moby/swarmkit/v2/remotes" "github.com/moby/swarmkit/v2/xnet" "github.com/pkg/errors" @@ -134,7 +134,7 @@ type Config struct { Availability api.NodeSpec_Availability // PluginGetter provides access to docker's plugin inventory. - PluginGetter plugingetter.PluginGetter + PluginGetter plugin.Getter // FIPS is a boolean stating whether the node is FIPS enabled FIPS bool diff --git a/node/plugin/pluginapi.go b/node/plugin/pluginapi.go new file mode 100644 index 0000000000..2758e0a178 --- /dev/null +++ b/node/plugin/pluginapi.go @@ -0,0 +1,23 @@ +package plugin + +import "net" + +type Plugin interface { + Name() string + ScopedPath(string) string + Client() Client +} + +type AddrPlugin interface { + Plugin + Addr() net.Addr +} + +type Client interface { + Call(method string, args, ret interface{}) error +} + +type Getter interface { + Get(name, capability string) (Plugin, error) + GetAllManagedPluginsByCap(capability string) []Plugin +} diff --git a/testutils/fake_plugingetter.go b/testutils/fake_plugingetter.go index f134b3b3ab..9969ec84c2 100644 --- a/testutils/fake_plugingetter.go +++ b/testutils/fake_plugingetter.go @@ -3,20 +3,20 @@ package testutils import ( "fmt" "net" - "time" - "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/pkg/plugins" + "github.com/moby/swarmkit/v2/node/plugin" ) const DockerCSIPluginNodeCap = "csinode" const DockerCSIPluginControllerCap = "csicontroller" type FakePluginGetter struct { - Plugins map[string]*FakeCompatPlugin + Plugins map[string]*FakePlugin } -func (f *FakePluginGetter) Get(name, capability string, _ int) (plugingetter.CompatPlugin, error) { +var _ plugin.Getter = &FakePluginGetter{} + +func (f *FakePluginGetter) Get(name, capability string) (plugin.Plugin, error) { if capability != DockerCSIPluginNodeCap && capability != DockerCSIPluginControllerCap { return nil, fmt.Errorf( "requested plugin with %s cap, but should only ever request %s or %s", @@ -30,63 +30,43 @@ func (f *FakePluginGetter) Get(name, capability string, _ int) (plugingetter.Com return nil, fmt.Errorf("plugin %s not found", name) } -// GetAllByCap is not needed in the fake and is unimplemented -func (f *FakePluginGetter) GetAllByCap(_ string) ([]plugingetter.CompatPlugin, error) { - return nil, nil -} - // GetAllManagedPluginsByCap returns all of the fake's plugins. If capability // is anything other than DockerCSIPluginCap, it returns nothing. -func (f *FakePluginGetter) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin { +func (f *FakePluginGetter) GetAllManagedPluginsByCap(capability string) []plugin.Plugin { if capability != DockerCSIPluginNodeCap && capability != DockerCSIPluginControllerCap { return nil } - allPlugins := make([]plugingetter.CompatPlugin, 0, len(f.Plugins)) + allPlugins := make([]plugin.Plugin, 0, len(f.Plugins)) for _, plug := range f.Plugins { allPlugins = append(allPlugins, plug) } return allPlugins } -// Handle is not needed in the fake, so is unimplemented. -func (f *FakePluginGetter) Handle(_ string, _ func(string, *plugins.Client)) {} - -// fakeCompatPlugin is a fake implementing the plugingetter.CompatPlugin and -// plugingetter.PluginAddr interfaces -type FakeCompatPlugin struct { +type FakePlugin struct { PluginName string PluginAddr net.Addr Scope string } -func (f *FakeCompatPlugin) Name() string { +var _ plugin.AddrPlugin = &FakePlugin{} + +func (f *FakePlugin) Name() string { return f.PluginName } -func (f *FakeCompatPlugin) ScopedPath(path string) string { +func (f *FakePlugin) ScopedPath(path string) string { if f.Scope != "" { return fmt.Sprintf("%s/%s", f.Scope, path) } return path } -func (f *FakeCompatPlugin) IsV1() bool { - return false -} - -func (f *FakeCompatPlugin) Client() *plugins.Client { +func (f *FakePlugin) Client() plugin.Client { return nil } -func (f *FakeCompatPlugin) Addr() net.Addr { +func (f *FakePlugin) Addr() net.Addr { return f.PluginAddr } - -func (f *FakeCompatPlugin) Timeout() time.Duration { - return time.Second -} - -func (f *FakeCompatPlugin) Protocol() string { - return "" -}