diff --git a/helper/testhelpers/pluginhelpers/pluginhelpers.go b/helper/testhelpers/pluginhelpers/pluginhelpers.go new file mode 100644 index 000000000..4ce0d9357 --- /dev/null +++ b/helper/testhelpers/pluginhelpers/pluginhelpers.go @@ -0,0 +1,139 @@ +// Package pluginhelpers contains testhelpers that don't depend on package +// vault, and thus can be used within vault (as well as elsewhere.) +package pluginhelpers + +import ( + "crypto/sha256" + "fmt" + "os" + "os/exec" + "path" + "path/filepath" + "sync" + + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/mitchellh/go-testing-interface" +) + +var ( + testPluginCacheLock sync.Mutex + testPluginCache = map[string][]byte{} +) + +type TestPlugin struct { + Name string + Typ consts.PluginType + Version string + FileName string + Sha256 string +} + +func GetPlugin(t testing.T, typ consts.PluginType) (string, string, string, string) { + t.Helper() + var pluginName string + var pluginType string + var pluginMain string + var pluginVersionLocation string + + switch typ { + case consts.PluginTypeCredential: + pluginType = "approle" + pluginName = "vault-plugin-auth-" + pluginType + pluginMain = filepath.Join("builtin", "credential", pluginType, "cmd", pluginType, "main.go") + pluginVersionLocation = fmt.Sprintf("github.com/hashicorp/vault/builtin/credential/%s.ReportedVersion", pluginType) + case consts.PluginTypeSecrets: + pluginType = "consul" + pluginName = "vault-plugin-secrets-" + pluginType + pluginMain = filepath.Join("builtin", "logical", pluginType, "cmd", pluginType, "main.go") + pluginVersionLocation = fmt.Sprintf("github.com/hashicorp/vault/builtin/logical/%s.ReportedVersion", pluginType) + case consts.PluginTypeDatabase: + pluginType = "postgresql" + pluginName = "vault-plugin-database-" + pluginType + pluginMain = filepath.Join("plugins", "database", pluginType, fmt.Sprintf("%s-database-plugin", pluginType), "main.go") + pluginVersionLocation = fmt.Sprintf("github.com/hashicorp/vault/plugins/database/%s.ReportedVersion", pluginType) + default: + t.Fatal(typ.String()) + } + return pluginName, pluginType, pluginMain, pluginVersionLocation +} + +// to mount a plugin, we need a working binary plugin, so we compile one here. +// pluginVersion is used to override the plugin's self-reported version +func CompilePlugin(t testing.T, typ consts.PluginType, pluginVersion string, pluginDir string) TestPlugin { + t.Helper() + + pluginName, pluginType, pluginMain, pluginVersionLocation := GetPlugin(t, typ) + + testPluginCacheLock.Lock() + defer testPluginCacheLock.Unlock() + + var pluginBytes []byte + + dir := "" + var err error + for { + dir, err = os.Getwd() + if err != nil { + t.Fatal(err) + } + // detect if we are in a subdirectory or the root directory and compensate + if _, err := os.Stat("builtin"); os.IsNotExist(err) { + err := os.Chdir("..") + if err != nil { + t.Fatal(err) + } + } else { + break + } + } + + pluginPath := path.Join(pluginDir, pluginName) + if pluginVersion != "" { + pluginPath += "-" + pluginVersion + } + + key := fmt.Sprintf("%s %s %s", pluginName, pluginType, pluginVersion) + // cache the compilation to only run once + var ok bool + pluginBytes, ok = testPluginCache[key] + if !ok { + // we need to compile + line := []string{"build"} + if pluginVersion != "" { + line = append(line, "-ldflags", fmt.Sprintf("-X %s=%s", pluginVersionLocation, pluginVersion)) + } + line = append(line, "-o", pluginPath, pluginMain) + cmd := exec.Command("go", line...) + cmd.Dir = dir + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(fmt.Errorf("error running go build %v output: %s", err, output)) + } + testPluginCache[key], err = os.ReadFile(pluginPath) + if err != nil { + t.Fatal(err) + } + pluginBytes = testPluginCache[key] + } + + // write the cached plugin if necessary + if _, err := os.Stat(pluginPath); os.IsNotExist(err) { + err = os.WriteFile(pluginPath, pluginBytes, 0o755) + } + if err != nil { + t.Fatal(err) + } + + sha := sha256.New() + _, err = sha.Write(pluginBytes) + if err != nil { + t.Fatal(err) + } + return TestPlugin{ + Name: pluginName, + Typ: typ, + Version: pluginVersion, + FileName: path.Base(pluginPath), + Sha256: fmt.Sprintf("%x", sha.Sum(nil)), + } +} diff --git a/vault/external_plugin_test.go b/vault/external_plugin_test.go index a604cd44d..22bb93aa3 100644 --- a/vault/external_plugin_test.go +++ b/vault/external_plugin_test.go @@ -2,20 +2,19 @@ package vault import ( "context" - "crypto/sha256" "encoding/hex" "errors" "fmt" "os" - "os/exec" "path" "path/filepath" "strings" - "sync" "testing" + log "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/testhelpers/corehelpers" + "github.com/hashicorp/vault/helper/testhelpers/pluginhelpers" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/pluginutil" @@ -27,28 +26,15 @@ import ( const vaultTestingMockPluginEnv = "VAULT_TESTING_MOCK_PLUGIN" -var ( - pluginCacheLock sync.Mutex - pluginCache = map[string][]byte{} -) - -type testPlugin struct { - name string - typ consts.PluginType - version string - fileName string - sha256 string -} - // version is used to override the plugin's self-reported version -func testCoreWithPlugins(t *testing.T, typ consts.PluginType, versions ...string) (*Core, []testPlugin) { +func testCoreWithPlugins(t *testing.T, typ consts.PluginType, versions ...string) (*Core, []pluginhelpers.TestPlugin) { t.Helper() pluginDir, cleanup := corehelpers.MakeTestPluginDir(t) t.Cleanup(func() { cleanup(t) }) - var plugins []testPlugin + var plugins []pluginhelpers.TestPlugin for _, version := range versions { - plugins = append(plugins, compilePlugin(t, typ, version, pluginDir)) + plugins = append(plugins, pluginhelpers.CompilePlugin(t, typ, version, pluginDir)) } conf := &CoreConfig{ BuiltinRegistry: corehelpers.NewMockBuiltinRegistry(), @@ -59,109 +45,6 @@ func testCoreWithPlugins(t *testing.T, typ consts.PluginType, versions ...string return core, plugins } -func getPlugin(t *testing.T, typ consts.PluginType) (string, string, string, string) { - t.Helper() - var pluginName string - var pluginType string - var pluginMain string - var pluginVersionLocation string - - switch typ { - case consts.PluginTypeCredential: - pluginType = "approle" - pluginName = "vault-plugin-auth-" + pluginType - pluginMain = filepath.Join("builtin", "credential", pluginType, "cmd", pluginType, "main.go") - pluginVersionLocation = fmt.Sprintf("github.com/hashicorp/vault/builtin/credential/%s.ReportedVersion", pluginType) - case consts.PluginTypeSecrets: - pluginType = "consul" - pluginName = "vault-plugin-secrets-" + pluginType - pluginMain = filepath.Join("builtin", "logical", pluginType, "cmd", pluginType, "main.go") - pluginVersionLocation = fmt.Sprintf("github.com/hashicorp/vault/builtin/logical/%s.ReportedVersion", pluginType) - case consts.PluginTypeDatabase: - pluginType = "postgresql" - pluginName = "vault-plugin-database-" + pluginType - pluginMain = filepath.Join("plugins", "database", pluginType, fmt.Sprintf("%s-database-plugin", pluginType), "main.go") - pluginVersionLocation = fmt.Sprintf("github.com/hashicorp/vault/plugins/database/%s.ReportedVersion", pluginType) - default: - t.Fatal(typ.String()) - } - return pluginName, pluginType, pluginMain, pluginVersionLocation -} - -// to mount a plugin, we need a working binary plugin, so we compile one here. -// pluginVersion is used to override the plugin's self-reported version -func compilePlugin(t *testing.T, typ consts.PluginType, pluginVersion string, pluginDir string) testPlugin { - t.Helper() - - pluginName, pluginType, pluginMain, pluginVersionLocation := getPlugin(t, typ) - - pluginCacheLock.Lock() - defer pluginCacheLock.Unlock() - - var pluginBytes []byte - - dir := "" - // detect if we are in the "vault/" or the root directory and compensate - if _, err := os.Stat("builtin"); os.IsNotExist(err) { - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - dir = filepath.Dir(wd) - } - - pluginPath := path.Join(pluginDir, pluginName) - if pluginVersion != "" { - pluginPath += "-" + pluginVersion - } - - key := fmt.Sprintf("%s %s %s", pluginName, pluginType, pluginVersion) - // cache the compilation to only run once - var ok bool - pluginBytes, ok = pluginCache[key] - if !ok { - // we need to compile - line := []string{"build"} - if pluginVersion != "" { - line = append(line, "-ldflags", fmt.Sprintf("-X %s=%s", pluginVersionLocation, pluginVersion)) - } - line = append(line, "-o", pluginPath, pluginMain) - cmd := exec.Command("go", line...) - cmd.Dir = dir - output, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(fmt.Errorf("error running go build %v output: %s", err, output)) - } - pluginCache[key], err = os.ReadFile(pluginPath) - if err != nil { - t.Fatal(err) - } - pluginBytes = pluginCache[key] - } - - // write the cached plugin if necessary - var err error - if _, err := os.Stat(pluginPath); os.IsNotExist(err) { - err = os.WriteFile(pluginPath, pluginBytes, 0o755) - } - if err != nil { - t.Fatal(err) - } - - sha := sha256.New() - _, err = sha.Write(pluginBytes) - if err != nil { - t.Fatal(err) - } - return testPlugin{ - name: pluginName, - typ: typ, - version: pluginVersion, - fileName: path.Base(pluginPath), - sha256: fmt.Sprintf("%x", sha.Sum(nil)), - } -} - func TestCore_EnableExternalPlugin(t *testing.T) { for name, tc := range map[string]struct { pluginType consts.PluginType @@ -180,10 +63,31 @@ func TestCore_EnableExternalPlugin(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - c, plugins := testCoreWithPlugins(t, tc.pluginType, "") - registerPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType.String(), "1.0.0", plugins[0].sha256, plugins[0].fileName) + coreConfig := &CoreConfig{ + DisableMlock: true, + DisableCache: true, + Logger: log.NewNullLogger(), + CredentialBackends: map[string]logical.Factory{}, + } - mountPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType, "v1.0.0", "") + cluster := NewTestCluster(t, coreConfig, &TestClusterOptions{ + Plugins: &TestPluginConfig{ + Typ: tc.pluginType, + Versions: []string{""}, + }, + }) + + cluster.Start() + defer cluster.Cleanup() + + c := cluster.Cores[0].Core + TestWaitActive(t, c) + + plugins := cluster.Plugins + + registerPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType.String(), "1.0.0", plugins[0].Sha256, plugins[0].FileName) + + mountPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType, "v1.0.0", "") match := c.router.MatchingMount(namespace.RootContext(nil), tc.routerPath) if match != tc.expectedMatch { @@ -254,10 +158,10 @@ func TestCore_EnableExternalPlugin_MultipleVersions(t *testing.T) { t.Run(name, func(t *testing.T) { c, plugins := testCoreWithPlugins(t, tc.pluginType, "") for _, version := range tc.registerVersions { - registerPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType.String(), version, plugins[0].sha256, plugins[0].fileName) + registerPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType.String(), version, plugins[0].Sha256, plugins[0].FileName) } - mountPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType, tc.mountVersion, "") + mountPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType, tc.mountVersion, "") match := c.router.MatchingMount(namespace.RootContext(nil), tc.routerPath) if match != tc.expectedMatch { @@ -286,8 +190,8 @@ func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) { // create an external plugin to shadow the builtin "pending-removal-test-plugin" pluginName := "therug" - plugin := compilePlugin(t, consts.PluginTypeCredential, "", pluginDir) - err := os.Link(path.Join(pluginDir, plugin.fileName), path.Join(pluginDir, pluginName)) + plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "", pluginDir) + err := os.Link(path.Join(pluginDir, plugin.FileName), path.Join(pluginDir, pluginName)) if err != nil { t.Fatal(err) } @@ -300,7 +204,7 @@ func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) { c, keys, root := testCoreUnsealed(t, c) // Register a plugin - registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "", plugin.sha256, plugin.fileName) + registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "", plugin.Sha256, plugin.FileName) mountPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential, "", "") plugct := len(c.pluginCatalog.externalPlugins) if plugct != 1 { @@ -358,8 +262,8 @@ func TestCore_Unseal_isMajorVersionFirstMount_PendingRemoval_Plugin(t *testing.T // create an external plugin to shadow the builtin "pending-removal-test-plugin" pluginName := "pending-removal-test-plugin" - plugin := compilePlugin(t, consts.PluginTypeCredential, "", pluginDir) - err := os.Link(path.Join(pluginDir, plugin.fileName), path.Join(pluginDir, pluginName)) + plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "", pluginDir) + err := os.Link(path.Join(pluginDir, plugin.FileName), path.Join(pluginDir, pluginName)) if err != nil { t.Fatal(err) } @@ -371,11 +275,11 @@ func TestCore_Unseal_isMajorVersionFirstMount_PendingRemoval_Plugin(t *testing.T c, keys, root := testCoreUnsealed(t, c) // Register a shadow plugin - registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "", plugin.sha256, plugin.fileName) + registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "", plugin.Sha256, plugin.FileName) mountPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential, "", "") // Deregister shadow plugin - deregisterPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "", plugin.sha256, plugin.fileName) + deregisterPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "", plugin.Sha256, plugin.FileName) // Make sure this isn't the first mount for the current major version. if c.isMajorVersionFirstMount(context.Background()) { @@ -437,8 +341,8 @@ func TestCore_EnableExternalPlugin_PendingRemoval(t *testing.T) { // create an external plugin to shadow the builtin "pending-removal-test-plugin" pluginName := "pending-removal-test-plugin" - plugin := compilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir) - err := os.Link(path.Join(pluginDir, plugin.fileName), path.Join(pluginDir, pluginName)) + plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir) + err := os.Link(path.Join(pluginDir, plugin.FileName), path.Join(pluginDir, pluginName)) if err != nil { t.Fatal(err) } @@ -462,7 +366,7 @@ func TestCore_EnableExternalPlugin_PendingRemoval(t *testing.T) { } // Register a shadow plugin - registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "v1.2.3", plugin.sha256, plugin.fileName) + registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "v1.2.3", plugin.Sha256, plugin.FileName) mountPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential, "", "") } @@ -471,8 +375,8 @@ func TestCore_EnableExternalPlugin_ShadowBuiltin(t *testing.T) { t.Cleanup(func() { cleanup(t) }) // create an external plugin to shadow the builtin "approle" - plugin := compilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir) - err := os.Link(path.Join(pluginDir, plugin.fileName), path.Join(pluginDir, "approle")) + plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir) + err := os.Link(path.Join(pluginDir, plugin.FileName), path.Join(pluginDir, "approle")) if err != nil { t.Fatal(err) } @@ -511,7 +415,7 @@ func TestCore_EnableExternalPlugin_ShadowBuiltin(t *testing.T) { } // Register a shadow plugin - registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "v1.2.3", plugin.sha256, plugin.fileName) + registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "v1.2.3", plugin.Sha256, plugin.FileName) // Verify auth table hasn't changed if err := verifyAuthListDeprecationStatus(pluginName, true); err != nil { @@ -528,7 +432,7 @@ func TestCore_EnableExternalPlugin_ShadowBuiltin(t *testing.T) { } // Deregister shadow plugin - deregisterPlugin(t, c.systemBackend, pluginName, consts.PluginTypeSecrets.String(), "v1.2.3", plugin.sha256, plugin.fileName) + deregisterPlugin(t, c.systemBackend, pluginName, consts.PluginTypeSecrets.String(), "v1.2.3", plugin.Sha256, plugin.FileName) // Verify auth table hasn't changed if err := verifyAuthListDeprecationStatus(pluginName, false); err != nil { @@ -550,8 +454,8 @@ func TestCore_EnableExternalKv_MultipleVersions(t *testing.T) { t.Cleanup(func() { cleanup(t) }) // new kv plugin can be registered but not mounted - plugin := compilePlugin(t, consts.PluginTypeSecrets, "v1.2.3", pluginDir) - err := os.Link(path.Join(pluginDir, plugin.fileName), path.Join(pluginDir, "kv")) + plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeSecrets, "v1.2.3", pluginDir) + err := os.Link(path.Join(pluginDir, plugin.FileName), path.Join(pluginDir, "kv")) if err != nil { t.Fatal(err) } @@ -563,7 +467,7 @@ func TestCore_EnableExternalKv_MultipleVersions(t *testing.T) { c := TestCoreWithSealAndUI(t, conf) c, _, _ = testCoreUnsealed(t, c) - registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeSecrets.String(), "v1.2.3", plugin.sha256, plugin.fileName) + registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeSecrets.String(), "v1.2.3", plugin.Sha256, plugin.FileName) req := logical.TestRequest(t, logical.ReadOperation, "plugins/catalog") resp, err := c.systemBackend.HandleRequest(namespace.RootContext(nil), req) if err != nil { @@ -603,8 +507,8 @@ func TestCore_EnableExternalNoop_MultipleVersions(t *testing.T) { t.Cleanup(func() { cleanup(t) }) // new noop plugin can be registered but not mounted - plugin := compilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir) - err := os.Link(path.Join(pluginDir, plugin.fileName), path.Join(pluginDir, "noop")) + plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir) + err := os.Link(path.Join(pluginDir, plugin.FileName), path.Join(pluginDir, "noop")) if err != nil { t.Fatal(err) } @@ -616,7 +520,7 @@ func TestCore_EnableExternalNoop_MultipleVersions(t *testing.T) { c := TestCoreWithSealAndUI(t, conf) c, _, _ = testCoreUnsealed(t, c) - registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "v1.2.3", plugin.sha256, plugin.fileName) + registerPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential.String(), "v1.2.3", plugin.Sha256, plugin.FileName) req := logical.TestRequest(t, logical.ReadOperation, "plugins/catalog") resp, err := c.systemBackend.HandleRequest(namespace.RootContext(nil), req) if err != nil { @@ -674,10 +578,10 @@ func TestCore_EnableExternalPlugin_NoVersionsOkay(t *testing.T) { // version specified should mount the unversioned plugin even if there // are versioned plugins available. for _, version := range []string{"", "v1.0.0"} { - registerPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType.String(), version, plugins[0].sha256, plugins[0].fileName) + registerPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType.String(), version, plugins[0].Sha256, plugins[0].FileName) } - mountPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType, "", "") + mountPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType, "", "") match := c.router.MatchingMount(namespace.RootContext(nil), tc.routerPath) if match != tc.expectedMatch { @@ -711,11 +615,11 @@ func TestCore_EnableExternalCredentialPlugin_NoVersionOnRegister(t *testing.T) { } { t.Run(name, func(t *testing.T) { c, plugins := testCoreWithPlugins(t, tc.pluginType, "") - registerPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType.String(), "", plugins[0].sha256, plugins[0].fileName) + registerPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType.String(), "", plugins[0].Sha256, plugins[0].FileName) req := logical.TestRequest(t, logical.UpdateOperation, mountTable(tc.pluginType)) req.Data = map[string]interface{}{ - "type": plugins[0].name, + "type": plugins[0].Name, "config": map[string]interface{}{ "plugin_version": "v1.0.0", }, @@ -743,10 +647,10 @@ func TestCore_EnableExternalCredentialPlugin_InvalidName(t *testing.T) { c, plugins := testCoreWithPlugins(t, tc.pluginType, "") d := &framework.FieldData{ Raw: map[string]interface{}{ - "name": plugins[0].name, - "sha256": plugins[0].sha256, + "name": plugins[0].Name, + "sha256": plugins[0].Sha256, "version": "v1.0.0", - "command": plugins[0].name + "xyz", + "command": plugins[0].Name + "xyz", }, Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields, } @@ -778,12 +682,12 @@ func TestExternalPlugin_getBackendTypeVersion(t *testing.T) { } { t.Run(name, func(t *testing.T) { c, plugins := testCoreWithPlugins(t, tc.pluginType, tc.setRunningVersion) - registerPlugin(t, c.systemBackend, plugins[0].name, tc.pluginType.String(), tc.setRunningVersion, plugins[0].sha256, plugins[0].fileName) + registerPlugin(t, c.systemBackend, plugins[0].Name, tc.pluginType.String(), tc.setRunningVersion, plugins[0].Sha256, plugins[0].FileName) - shaBytes, _ := hex.DecodeString(plugins[0].sha256) - commandFull := filepath.Join(c.pluginCatalog.directory, plugins[0].fileName) + shaBytes, _ := hex.DecodeString(plugins[0].Sha256) + commandFull := filepath.Join(c.pluginCatalog.directory, plugins[0].FileName) entry := &pluginutil.PluginRunner{ - Name: plugins[0].name, + Name: plugins[0].Name, Command: commandFull, Args: nil, Sha256: shaBytes, @@ -843,13 +747,13 @@ func TestExternalPlugin_CheckFilePermissions(t *testing.T) { } { t.Run(name, func(t *testing.T) { c, plugins := testCoreWithPlugins(t, tc.pluginType, tc.pluginVersion) - registeredPluginName := fmt.Sprintf(tc.pluginNameFmt, plugins[0].name) + registeredPluginName := fmt.Sprintf(tc.pluginNameFmt, plugins[0].Name) // Permissions will be checked once during registration. req := logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("plugins/catalog/%s/%s", tc.pluginType.String(), registeredPluginName)) req.Data = map[string]interface{}{ - "command": plugins[0].fileName, - "sha256": plugins[0].sha256, + "command": plugins[0].FileName, + "sha256": plugins[0].Sha256, "version": tc.pluginVersion, } resp, err := c.systemBackend.HandleRequest(namespace.RootContext(nil), req) diff --git a/vault/testing.go b/vault/testing.go index cb3ff355d..da3c72e33 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -41,6 +41,7 @@ import ( "github.com/hashicorp/vault/helper/metricsutil" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/testhelpers/corehelpers" + "github.com/hashicorp/vault/helper/testhelpers/pluginhelpers" "github.com/hashicorp/vault/internalshared/configutil" v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5" "github.com/hashicorp/vault/sdk/framework" @@ -772,6 +773,7 @@ type TestCluster struct { CAKeyPEM []byte Cores []*TestClusterCore ID string + Plugins []pluginhelpers.TestPlugin RootToken string RootCAs *x509.CertPool TempDir string @@ -1182,6 +1184,13 @@ type TestClusterOptions struct { EffectiveSDKVersionMap map[int]string NoDefaultQuotas bool + + Plugins *TestPluginConfig +} + +type TestPluginConfig struct { + Typ consts.PluginType + Versions []string } var DefaultNumCores = 3 @@ -1615,6 +1624,23 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te opts.ClusterLayers = inmemCluster } + if opts != nil && opts.Plugins != nil { + var pluginDir string + var cleanup func(t testing.T) + + if coreConfig.PluginDirectory == "" { + pluginDir, cleanup = corehelpers.MakeTestPluginDir(t) + coreConfig.PluginDirectory = pluginDir + t.Cleanup(func() { cleanup(t) }) + } + + var plugins []pluginhelpers.TestPlugin + for _, version := range opts.Plugins.Versions { + plugins = append(plugins, pluginhelpers.CompilePlugin(t, opts.Plugins.Typ, version, coreConfig.PluginDirectory)) + } + testCluster.Plugins = plugins + } + // Create cores testCluster.cleanupFuncs = []func(){} cores := []*Core{}