124 lines
4.4 KiB
Go
124 lines
4.4 KiB
Go
|
package drivermanager
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
log "github.com/hashicorp/go-hclog"
|
||
|
"github.com/hashicorp/go-plugin"
|
||
|
"github.com/hashicorp/nomad/helper/pluginutils/loader"
|
||
|
"github.com/hashicorp/nomad/helper/pluginutils/singleton"
|
||
|
"github.com/hashicorp/nomad/helper/testlog"
|
||
|
"github.com/hashicorp/nomad/plugins/base"
|
||
|
dtu "github.com/hashicorp/nomad/plugins/drivers/testutils"
|
||
|
"github.com/stretchr/testify/mock"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
type mockedCatalog struct {
|
||
|
mock.Mock
|
||
|
}
|
||
|
|
||
|
func (m *mockedCatalog) Dispense(name, pluginType string, cfg *base.AgentConfig, logger log.Logger) (loader.PluginInstance, error) {
|
||
|
args := m.Called(name, pluginType, cfg, logger)
|
||
|
return loader.MockBasicExternalPlugin(&dtu.MockDriver{}, "0.1.0"), args.Error(0)
|
||
|
}
|
||
|
|
||
|
func (m *mockedCatalog) Reattach(name, pluginType string, config *plugin.ReattachConfig) (loader.PluginInstance, error) {
|
||
|
args := m.Called(name, pluginType, config)
|
||
|
return loader.MockBasicExternalPlugin(&dtu.MockDriver{}, "0.1.0"), args.Error(0)
|
||
|
}
|
||
|
|
||
|
func (m *mockedCatalog) Catalog() map[string][]*base.PluginInfoResponse {
|
||
|
m.Called()
|
||
|
return map[string][]*base.PluginInfoResponse{
|
||
|
base.PluginTypeDriver: {&base.PluginInfoResponse{Name: "mock", Type: base.PluginTypeDriver}},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *mockedCatalog) resetMock() {
|
||
|
m.ExpectedCalls = []*mock.Call{}
|
||
|
m.Calls = []mock.Call{}
|
||
|
}
|
||
|
|
||
|
func TestInstanceManager_dispense(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
cat := new(mockedCatalog)
|
||
|
cat.Test(t)
|
||
|
var fetchRet bool
|
||
|
i := &instanceManager{
|
||
|
logger: testlog.HCLogger(t),
|
||
|
ctx: ctx,
|
||
|
cancel: cancel,
|
||
|
loader: cat,
|
||
|
storeReattach: func(*plugin.ReattachConfig) error { return nil },
|
||
|
fetchReattach: func() (*plugin.ReattachConfig, bool) { return nil, fetchRet },
|
||
|
pluginConfig: &base.AgentConfig{},
|
||
|
id: &loader.PluginID{Name: "mock", PluginType: base.PluginTypeDriver},
|
||
|
updateNodeFromDriver: noopUpdater,
|
||
|
eventHandlerFactory: noopEventHandlerFactory,
|
||
|
firstFingerprintCh: make(chan struct{}),
|
||
|
}
|
||
|
require := require.New(t)
|
||
|
|
||
|
// First test the happy path, no reattach config is stored, plugin dispenses without error
|
||
|
fetchRet = false
|
||
|
cat.On("Dispense", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||
|
plug, err := i.dispense()
|
||
|
require.NoError(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 1)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 0)
|
||
|
|
||
|
// Dispensing a second time should not dispense a new plugin from the catalog, but reuse the existing
|
||
|
plug2, err := i.dispense()
|
||
|
require.NoError(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 1)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 0)
|
||
|
require.Same(plug, plug2)
|
||
|
|
||
|
// If the plugin has exited test that the manager attempts to retry dispense
|
||
|
cat.resetMock()
|
||
|
i.plugin = nil
|
||
|
cat.On("Dispense", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(singleton.SingletonPluginExited)
|
||
|
_, err = i.dispense()
|
||
|
require.Error(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 2)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 0)
|
||
|
|
||
|
// Test that when a reattach config exists it attempts plugin reattachment
|
||
|
// First case is when plugin reattachment is successful
|
||
|
fetchRet = true
|
||
|
cat.resetMock()
|
||
|
i.plugin = nil
|
||
|
cat.On("Dispense", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||
|
cat.On("Reattach", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||
|
plug, err = i.dispense()
|
||
|
require.NoError(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 0)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 1)
|
||
|
// Dispensing a second time should not dispense a new plugin from the catalog
|
||
|
plug2, err = i.dispense()
|
||
|
require.NoError(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 0)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 1)
|
||
|
require.Same(plug, plug2)
|
||
|
|
||
|
// Finally test when reattachment fails. A new plugin should be dispensed
|
||
|
cat.resetMock()
|
||
|
i.plugin = nil
|
||
|
cat.On("Dispense", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||
|
cat.On("Reattach", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("failed to dispense"))
|
||
|
plug, err = i.dispense()
|
||
|
require.NoError(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 1)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 1)
|
||
|
// Dispensing a second time should not dispense a new plugin from the catalog
|
||
|
plug2, err = i.dispense()
|
||
|
require.NoError(err)
|
||
|
cat.AssertNumberOfCalls(t, "Dispense", 1)
|
||
|
cat.AssertNumberOfCalls(t, "Reattach", 1)
|
||
|
require.Same(plug, plug2)
|
||
|
|
||
|
}
|