mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 10:12:35 +00:00
Refactor plugin catalog and plugin runtime catalog into their own package (#24403)
* Refactor plugin catalog into its own package * Fix some unnecessarily slow tests due to accidentally running multiple plugin processes * Clean up MakeTestPluginDir helper * Move getBackendVersion tests to plugin catalog package * Use corehelpers.MakeTestPlugin consistently * Fix semgrep failure: check for nil value from logical.Storage
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||||
vaulthttp "github.com/hashicorp/vault/http"
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
"github.com/hashicorp/vault/plugins/database/postgresql"
|
"github.com/hashicorp/vault/plugins/database/postgresql"
|
||||||
@@ -35,11 +36,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getClusterPostgresDB(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
func getClusterPostgresDB(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
||||||
|
t.Helper()
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"database": Factory,
|
"database": Factory,
|
||||||
},
|
},
|
||||||
BuiltinRegistry: builtinplugins.Registry,
|
BuiltinRegistry: builtinplugins.Registry,
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
@@ -52,17 +56,20 @@ func getClusterPostgresDB(t *testing.T) (*vault.TestCluster, logical.SystemView)
|
|||||||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||||
|
|
||||||
sys := vault.TestDynamicSystemView(cores[0].Core, nil)
|
sys := vault.TestDynamicSystemView(cores[0].Core, nil)
|
||||||
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_PostgresMultiplexed", []string{}, "")
|
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_PostgresMultiplexed", []string{})
|
||||||
|
|
||||||
return cluster, sys
|
return cluster, sys
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
||||||
|
t.Helper()
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"database": Factory,
|
"database": Factory,
|
||||||
},
|
},
|
||||||
BuiltinRegistry: builtinplugins.Registry,
|
BuiltinRegistry: builtinplugins.Registry,
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
@@ -1473,7 +1480,7 @@ func TestBackend_AsyncClose(t *testing.T) {
|
|||||||
// Test that having a plugin that takes a LONG time to close will not cause the cleanup function to take
|
// Test that having a plugin that takes a LONG time to close will not cause the cleanup function to take
|
||||||
// longer than 750ms.
|
// longer than 750ms.
|
||||||
cluster, sys := getCluster(t)
|
cluster, sys := getCluster(t)
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "hanging-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_Hanging", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "hanging-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_Hanging", []string{})
|
||||||
t.Cleanup(cluster.Cleanup)
|
t.Cleanup(cluster.Cleanup)
|
||||||
|
|
||||||
config := logical.TestBackendConfig()
|
config := logical.TestBackendConfig()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
vaulthttp "github.com/hashicorp/vault/http"
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
"github.com/hashicorp/vault/sdk/database/dbplugin"
|
"github.com/hashicorp/vault/sdk/database/dbplugin"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
@@ -106,14 +107,18 @@ func (m *mockPlugin) SetCredentials(ctx context.Context, statements dbplugin.Sta
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
||||||
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
|
t.Helper()
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
|
cluster := vault.NewTestCluster(t, &vault.CoreConfig{
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
|
}, &vault.TestClusterOptions{
|
||||||
HandlerFunc: vaulthttp.Handler,
|
HandlerFunc: vaulthttp.Handler,
|
||||||
})
|
})
|
||||||
cluster.Start()
|
cluster.Start()
|
||||||
cores := cluster.Cores
|
cores := cluster.Cores
|
||||||
|
|
||||||
sys := vault.TestDynamicSystemView(cores[0].Core, nil)
|
sys := vault.TestDynamicSystemView(cores[0].Core, nil)
|
||||||
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", consts.PluginTypeDatabase, "", "TestPlugin_GRPC_Main", []string{}, "")
|
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", consts.PluginTypeDatabase, "", "TestPlugin_GRPC_Main", []string{})
|
||||||
|
|
||||||
return cluster, sys
|
return cluster, sys
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ func TestPlugin_lifecycle(t *testing.T) {
|
|||||||
cluster, sys := getCluster(t)
|
cluster, sys := getCluster(t)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v4-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV4", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v4-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV4", []string{})
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV5", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV5", []string{})
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v6-database-plugin-muxed", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV6Multiplexed", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v6-database-plugin-muxed", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV6Multiplexed", []string{})
|
||||||
|
|
||||||
config := logical.TestBackendConfig()
|
config := logical.TestBackendConfig()
|
||||||
config.StorageView = &logical.InmemStorage{}
|
config.StorageView = &logical.InmemStorage{}
|
||||||
@@ -226,7 +226,7 @@ func TestPlugin_VersionSelection(t *testing.T) {
|
|||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
for _, version := range []string{"v11.0.0", "v11.0.1-rc1", "v2.0.0"} {
|
for _, version := range []string{"v11.0.0", "v11.0.1-rc1", "v2.0.0"} {
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, version, "TestBackend_PluginMain_MockV5", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, version, "TestBackend_PluginMain_MockV5", []string{})
|
||||||
}
|
}
|
||||||
|
|
||||||
config := logical.TestBackendConfig()
|
config := logical.TestBackendConfig()
|
||||||
@@ -312,11 +312,11 @@ func TestPlugin_VersionSelection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register a newer version of the plugin, and ensure that's the new default version selected.
|
// Register a newer version of the plugin, and ensure that's the new default version selected.
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "v11.0.1", "TestBackend_PluginMain_MockV5", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "v11.0.1", "TestBackend_PluginMain_MockV5", []string{})
|
||||||
t.Run("no version specified, new latest version selected", test(t, "", "v11.0.1"))
|
t.Run("no version specified, new latest version selected", test(t, "", "v11.0.1"))
|
||||||
|
|
||||||
// Register an unversioned plugin and ensure that is now selected when no version is specified.
|
// Register an unversioned plugin and ensure that is now selected when no version is specified.
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV5", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "", "TestBackend_PluginMain_MockV5", []string{})
|
||||||
for name, tc := range map[string]struct {
|
for name, tc := range map[string]struct {
|
||||||
selectVersion string
|
selectVersion string
|
||||||
expectedVersion string
|
expectedVersion string
|
||||||
@@ -397,7 +397,7 @@ func TestPlugin_VersionMustBeExplicitlyUpgraded(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register versioned plugin, and check that a new write to existing config doesn't upgrade the plugin implicitly.
|
// Register versioned plugin, and check that a new write to existing config doesn't upgrade the plugin implicitly.
|
||||||
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mysql-database-plugin", consts.PluginTypeDatabase, "v1.0.0", "TestBackend_PluginMain_MockV5", []string{}, "")
|
vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mysql-database-plugin", consts.PluginTypeDatabase, "v1.0.0", "TestBackend_PluginMain_MockV5", []string{})
|
||||||
resp, err = b.HandleRequest(context.Background(), &logical.Request{
|
resp, err = b.HandleRequest(context.Background(), &logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "config/db",
|
Path: "config/db",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/builtin/plugin"
|
"github.com/hashicorp/vault/builtin/plugin"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
vaulthttp "github.com/hashicorp/vault/http"
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/logging"
|
"github.com/hashicorp/vault/sdk/helper/logging"
|
||||||
@@ -115,7 +116,11 @@ func TestBackend_PluginMain_Multiplexed(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testConfig(t *testing.T, pluginCmd string) (*logical.BackendConfig, func()) {
|
func testConfig(t *testing.T, pluginCmd string) (*logical.BackendConfig, func()) {
|
||||||
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
|
t.Helper()
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
|
cluster := vault.NewTestCluster(t, &vault.CoreConfig{
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
|
}, &vault.TestClusterOptions{
|
||||||
HandlerFunc: vaulthttp.Handler,
|
HandlerFunc: vaulthttp.Handler,
|
||||||
})
|
})
|
||||||
cluster.Start()
|
cluster.Start()
|
||||||
@@ -137,7 +142,7 @@ func testConfig(t *testing.T, pluginCmd string) (*logical.BackendConfig, func())
|
|||||||
|
|
||||||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||||
|
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", pluginCmd, []string{}, "")
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", pluginCmd, []string{})
|
||||||
|
|
||||||
return config, func() {
|
return config, func() {
|
||||||
cluster.Cleanup()
|
cluster.Cleanup()
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ func TestAuthTuneCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration", func(t *testing.T) {
|
t.Run("integration", func(t *testing.T) {
|
||||||
t.Run("flags_all", func(t *testing.T) {
|
t.Run("flags_all", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration", func(t *testing.T) {
|
t.Run("integration", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
@@ -138,8 +137,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration with version", func(t *testing.T) {
|
t.Run("integration with version", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
@@ -186,8 +184,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration with missing version", func(t *testing.T) {
|
t.Run("integration with missing version", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
@@ -233,8 +230,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) {
|
|||||||
t.Run("deregister builtin", func(t *testing.T) {
|
t.Run("deregister builtin", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
|
|||||||
t.Run("default", func(t *testing.T) {
|
t.Run("default", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
@@ -116,8 +115,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
|
|||||||
t.Run("version flag", func(t *testing.T) {
|
t.Run("version flag", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
@@ -162,8 +160,7 @@ func TestPluginInfoCommand_Run(t *testing.T) {
|
|||||||
t.Run("field", func(t *testing.T) {
|
t.Run("field", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ func TestPluginRegisterCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration", func(t *testing.T) {
|
t.Run("integration", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
@@ -134,8 +133,7 @@ func TestPluginRegisterCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration with version", func(t *testing.T) {
|
t.Run("integration with version", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ func TestPluginReloadCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration", func(t *testing.T) {
|
t.Run("integration", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|||||||
@@ -152,8 +152,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) {
|
|||||||
t.Run("integration", func(t *testing.T) {
|
t.Run("integration", func(t *testing.T) {
|
||||||
t.Run("flags_all", func(t *testing.T) {
|
t.Run("flags_all", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
defer cleanup(t)
|
|
||||||
|
|
||||||
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
client, _, closer := testVaultServerPluginDir(t, pluginDir)
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import (
|
|||||||
sr "github.com/hashicorp/vault/serviceregistration"
|
sr "github.com/hashicorp/vault/serviceregistration"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
"github.com/hashicorp/vault/vault/hcp_link"
|
"github.com/hashicorp/vault/vault/hcp_link"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
vaultseal "github.com/hashicorp/vault/vault/seal"
|
vaultseal "github.com/hashicorp/vault/vault/seal"
|
||||||
"github.com/hashicorp/vault/version"
|
"github.com/hashicorp/vault/version"
|
||||||
"github.com/mitchellh/go-testing-interface"
|
"github.com/mitchellh/go-testing-interface"
|
||||||
@@ -3152,7 +3153,7 @@ func initDevCore(c *ServerCommand, coreConfig *vault.CoreConfig, config *server.
|
|||||||
for _, name := range list {
|
for _, name := range list {
|
||||||
path := filepath.Join(f.Name(), name)
|
path := filepath.Join(f.Name(), name)
|
||||||
if err := c.addPlugin(path, init.RootToken, core); err != nil {
|
if err := c.addPlugin(path, init.RootToken, core); err != nil {
|
||||||
if !errwrap.Contains(err, vault.ErrPluginBadType.Error()) {
|
if !errwrap.Contains(err, plugincatalog.ErrPluginBadType.Error()) {
|
||||||
return fmt.Errorf("Error enabling plugin %s: %s", name, err)
|
return fmt.Errorf("Error enabling plugin %s: %s", name, err)
|
||||||
}
|
}
|
||||||
pluginsNotLoaded = append(pluginsNotLoaded, name)
|
pluginsNotLoaded = append(pluginsNotLoaded, name)
|
||||||
|
|||||||
@@ -49,36 +49,27 @@ func RetryUntil(t testing.T, timeout time.Duration, f func() error) {
|
|||||||
|
|
||||||
// MakeTestPluginDir creates a temporary directory suitable for holding plugins.
|
// MakeTestPluginDir creates a temporary directory suitable for holding plugins.
|
||||||
// This helper also resolves symlinks to make tests happy on OS X.
|
// This helper also resolves symlinks to make tests happy on OS X.
|
||||||
func MakeTestPluginDir(t testing.T) (string, func(t testing.T)) {
|
func MakeTestPluginDir(t testing.T) string {
|
||||||
if t != nil {
|
t.Helper()
|
||||||
t.Helper()
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", "")
|
dir, err := os.MkdirTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if t == nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OSX tempdir are /var, but actually symlinked to /private/var
|
// OSX tempdir are /var, but actually symlinked to /private/var
|
||||||
dir, err = filepath.EvalSymlinks(dir)
|
dir, err = filepath.EvalSymlinks(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if t == nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dir, func(t testing.T) {
|
t.Cleanup(func() {
|
||||||
if err := os.RemoveAll(dir); err != nil {
|
if err := os.RemoveAll(dir); err != nil {
|
||||||
if t == nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockBuiltinRegistry() *mockBuiltinRegistry {
|
func NewMockBuiltinRegistry() *mockBuiltinRegistry {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
bplugin "github.com/hashicorp/vault/builtin/plugin"
|
bplugin "github.com/hashicorp/vault/builtin/plugin"
|
||||||
"github.com/hashicorp/vault/helper/benchhelpers"
|
"github.com/hashicorp/vault/helper/benchhelpers"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
@@ -25,7 +26,8 @@ import (
|
|||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluster, *vault.TestClusterCore) {
|
func getPluginClusterAndCore(t *testing.T, logger log.Logger) (*vault.TestCluster, *vault.TestClusterCore) {
|
||||||
|
t.Helper()
|
||||||
inm, err := inmem.NewTransactionalInmem(nil, logger)
|
inm, err := inmem.NewTransactionalInmem(nil, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -35,12 +37,14 @@ func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluste
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
Physical: inm,
|
Physical: inm,
|
||||||
HAPhysical: inmha.(physical.HABackend),
|
HAPhysical: inmha.(physical.HABackend),
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"plugin": bplugin.Factory,
|
"plugin": bplugin.Factory,
|
||||||
},
|
},
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster := vault.NewTestCluster(benchhelpers.TBtoT(t), coreConfig, &vault.TestClusterOptions{
|
cluster := vault.NewTestCluster(benchhelpers.TBtoT(t), coreConfig, &vault.TestClusterOptions{
|
||||||
@@ -54,7 +58,7 @@ func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluste
|
|||||||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||||
|
|
||||||
vault.TestWaitActive(benchhelpers.TBtoT(t), core.Core)
|
vault.TestWaitActive(benchhelpers.TBtoT(t), core.Core)
|
||||||
vault.TestAddTestPlugin(benchhelpers.TBtoT(t), core.Core, "mock-plugin", consts.PluginTypeSecrets, "", "TestPlugin_PluginMain", []string{}, "")
|
vault.TestAddTestPlugin(benchhelpers.TBtoT(t), core.Core, "mock-plugin", consts.PluginTypeSecrets, "", "TestPlugin_PluginMain", []string{})
|
||||||
|
|
||||||
// Mount the mock plugin
|
// Mount the mock plugin
|
||||||
err = core.Client.Sys().Mount("mock", &api.MountInput{
|
err = core.Client.Sys().Mount("mock", &api.MountInput{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -969,7 +971,7 @@ func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysV
|
|||||||
if entry.Version != "" {
|
if entry.Version != "" {
|
||||||
errContext += fmt.Sprintf(", version=%s", entry.Version)
|
errContext += fmt.Sprintf(", version=%s", entry.Version)
|
||||||
}
|
}
|
||||||
return nil, "", fmt.Errorf("%w: %s", ErrPluginNotFound, errContext)
|
return nil, "", fmt.Errorf("%w: %s", plugincatalog.ErrPluginNotFound, errContext)
|
||||||
}
|
}
|
||||||
if len(plug.Sha256) > 0 {
|
if len(plug.Sha256) > 0 {
|
||||||
runningSha = hex.EncodeToString(plug.Sha256)
|
runningSha = hex.EncodeToString(plug.Sha256)
|
||||||
@@ -1027,6 +1029,44 @@ func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysV
|
|||||||
return b, runningSha, nil
|
return b, runningSha, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapFactoryCheckPerms(core *Core, f logical.Factory) logical.Factory {
|
||||||
|
return func(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
|
||||||
|
pluginName := conf.Config["plugin_name"]
|
||||||
|
pluginVersion := conf.Config["plugin_version"]
|
||||||
|
pluginTypeRaw := conf.Config["plugin_type"]
|
||||||
|
pluginType, err := consts.ParsePluginType(pluginTypeRaw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginDescription := fmt.Sprintf("%s plugin %s", pluginTypeRaw, pluginName)
|
||||||
|
if pluginVersion != "" {
|
||||||
|
pluginDescription += " version " + pluginVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin, err := core.pluginCatalog.Get(ctx, pluginName, pluginType, pluginVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to find %s in plugin catalog: %w", pluginDescription, err)
|
||||||
|
}
|
||||||
|
if plugin == nil {
|
||||||
|
return nil, fmt.Errorf("failed to find %s in plugin catalog", pluginDescription)
|
||||||
|
}
|
||||||
|
if plugin.OCIImage != "" {
|
||||||
|
return f(ctx, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err := filepath.Rel(core.pluginDirectory, plugin.Command)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to compute plugin command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := core.CheckPluginPerms(command); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f(ctx, conf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// defaultAuthTable creates a default auth table
|
// defaultAuthTable creates a default auth table
|
||||||
func (c *Core) defaultAuthTable() *MountTable {
|
func (c *Core) defaultAuthTable() *MountTable {
|
||||||
table := &MountTable{
|
table := &MountTable{
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/shamir"
|
"github.com/hashicorp/vault/shamir"
|
||||||
"github.com/hashicorp/vault/vault/cluster"
|
"github.com/hashicorp/vault/vault/cluster"
|
||||||
"github.com/hashicorp/vault/vault/eventbus"
|
"github.com/hashicorp/vault/vault/eventbus"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/hashicorp/vault/vault/quotas"
|
"github.com/hashicorp/vault/vault/quotas"
|
||||||
vaultseal "github.com/hashicorp/vault/vault/seal"
|
vaultseal "github.com/hashicorp/vault/vault/seal"
|
||||||
"github.com/hashicorp/vault/version"
|
"github.com/hashicorp/vault/version"
|
||||||
@@ -94,6 +95,11 @@ const (
|
|||||||
// how policies should be applied
|
// how policies should be applied
|
||||||
coreGroupPolicyApplicationPath = "core/group-policy-application-mode"
|
coreGroupPolicyApplicationPath = "core/group-policy-application-mode"
|
||||||
|
|
||||||
|
// Path in storage for the plugin catalog.
|
||||||
|
pluginCatalogPath = "core/plugin-catalog/"
|
||||||
|
// Path in storage for the plugin runtime catalog.
|
||||||
|
pluginRuntimeCatalogPath = "core/plugin-runtime-catalog/"
|
||||||
|
|
||||||
// groupPolicyApplicationModeWithinNamespaceHierarchy is a configuration option for group
|
// groupPolicyApplicationModeWithinNamespaceHierarchy is a configuration option for group
|
||||||
// policy application modes, which allows only in-namespace-hierarchy policy application
|
// policy application modes, which allows only in-namespace-hierarchy policy application
|
||||||
groupPolicyApplicationModeWithinNamespaceHierarchy = "within_namespace_hierarchy"
|
groupPolicyApplicationModeWithinNamespaceHierarchy = "within_namespace_hierarchy"
|
||||||
@@ -242,7 +248,7 @@ type Core struct {
|
|||||||
|
|
||||||
// The registry of builtin plugins is passed in here as an interface because
|
// The registry of builtin plugins is passed in here as an interface because
|
||||||
// if it's used directly, it results in import cycles.
|
// if it's used directly, it results in import cycles.
|
||||||
builtinRegistry BuiltinRegistry
|
builtinRegistry plugincatalog.BuiltinRegistry
|
||||||
|
|
||||||
// N.B.: This is used to populate a dev token down replication, as
|
// N.B.: This is used to populate a dev token down replication, as
|
||||||
// otherwise, after replication is started, a dev would have to go through
|
// otherwise, after replication is started, a dev would have to go through
|
||||||
@@ -537,10 +543,10 @@ type Core struct {
|
|||||||
pluginFilePermissions int
|
pluginFilePermissions int
|
||||||
|
|
||||||
// pluginCatalog is used to manage plugin configurations
|
// pluginCatalog is used to manage plugin configurations
|
||||||
pluginCatalog *PluginCatalog
|
pluginCatalog *plugincatalog.PluginCatalog
|
||||||
|
|
||||||
// pluginRuntimeCatalog is used to manage plugin runtime configurations
|
// pluginRuntimeCatalog is used to manage plugin runtime configurations
|
||||||
pluginRuntimeCatalog *PluginRuntimeCatalog
|
pluginRuntimeCatalog *plugincatalog.PluginRuntimeCatalog
|
||||||
|
|
||||||
// The userFailedLoginInfo map has user failed login information.
|
// The userFailedLoginInfo map has user failed login information.
|
||||||
// It has user information (alias-name and mount accessor) as a key
|
// It has user information (alias-name and mount accessor) as a key
|
||||||
@@ -736,7 +742,7 @@ type CoreConfig struct {
|
|||||||
|
|
||||||
DevToken string
|
DevToken string
|
||||||
|
|
||||||
BuiltinRegistry BuiltinRegistry
|
BuiltinRegistry plugincatalog.BuiltinRegistry
|
||||||
|
|
||||||
LogicalBackends map[string]logical.Factory
|
LogicalBackends map[string]logical.Factory
|
||||||
|
|
||||||
@@ -2394,11 +2400,15 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.setupPluginRuntimeCatalog(ctx); err != nil {
|
if pluginRuntimeCatalog, err := plugincatalog.SetupPluginRuntimeCatalog(ctx, c.logger, NewBarrierView(c.barrier, pluginRuntimeCatalogPath)); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else {
|
||||||
|
c.pluginRuntimeCatalog = pluginRuntimeCatalog
|
||||||
}
|
}
|
||||||
if err := c.setupPluginCatalog(ctx); err != nil {
|
if pluginCatalog, err := plugincatalog.SetupPluginCatalog(ctx, c.logger, c.builtinRegistry, NewBarrierView(c.barrier, pluginCatalogPath), c.pluginDirectory, c.enableMlock, c.pluginRuntimeCatalog); err != nil {
|
||||||
return err
|
return err
|
||||||
|
} else {
|
||||||
|
c.pluginCatalog = pluginCatalog
|
||||||
}
|
}
|
||||||
if err := c.loadMounts(ctx); err != nil {
|
if err := c.loadMounts(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -3386,17 +3396,6 @@ func (c *Core) MetricSink() *metricsutil.ClusterMetricSink {
|
|||||||
return c.metricSink
|
return c.metricSink
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuiltinRegistry is an interface that allows the "vault" package to use
|
|
||||||
// the registry of builtin plugins without getting an import cycle. It
|
|
||||||
// also allows for mocking the registry easily.
|
|
||||||
type BuiltinRegistry interface {
|
|
||||||
Contains(name string, pluginType consts.PluginType) bool
|
|
||||||
Get(name string, pluginType consts.PluginType) (func() (interface{}, error), bool)
|
|
||||||
Keys(pluginType consts.PluginType) []string
|
|
||||||
DeprecationStatus(name string, pluginType consts.PluginType) (consts.DeprecationStatus, bool)
|
|
||||||
IsBuiltinEntPlugin(name string, pluginType consts.PluginType) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) AuditLogger() AuditLogger {
|
func (c *Core) AuditLogger() AuditLogger {
|
||||||
return &basicAuditor{c: c}
|
return &basicAuditor{c: c}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/hashicorp/vault/version"
|
"github.com/hashicorp/vault/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -283,7 +284,7 @@ func (d dynamicSystemView) LookupPluginVersion(ctx context.Context, name string,
|
|||||||
if version != "" {
|
if version != "" {
|
||||||
errContext += fmt.Sprintf(", version=%s", version)
|
errContext += fmt.Sprintf(", version=%s", version)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%w: %s", ErrPluginNotFound, errContext)
|
return nil, fmt.Errorf("%w: %s", plugincatalog.ErrPluginNotFound, errContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ package vault
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -15,8 +14,6 @@ import (
|
|||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginruntimeutil"
|
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,51 +108,6 @@ func mountAndUnmountContainerPlugin_WithRuntime(t *testing.T, c *Core, plugin pl
|
|||||||
routeRequest(false)
|
routeRequest(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExternalPluginInContainer_GetBackendTypeVersion(t *testing.T) {
|
|
||||||
c, plugins := testClusterWithContainerPlugins(t, []consts.PluginType{
|
|
||||||
consts.PluginTypeCredential,
|
|
||||||
consts.PluginTypeSecrets,
|
|
||||||
consts.PluginTypeDatabase,
|
|
||||||
})
|
|
||||||
for _, plugin := range plugins {
|
|
||||||
t.Run(plugin.Typ.String(), func(t *testing.T) {
|
|
||||||
for _, ociRuntime := range []string{"runc", "runsc"} {
|
|
||||||
t.Run(ociRuntime, func(t *testing.T) {
|
|
||||||
if _, err := exec.LookPath(ociRuntime); err != nil {
|
|
||||||
t.Skipf("Skipping test as %s not found on path", ociRuntime)
|
|
||||||
}
|
|
||||||
shaBytes, _ := hex.DecodeString(plugin.ImageSha256)
|
|
||||||
entry := &pluginutil.PluginRunner{
|
|
||||||
Name: plugin.Name,
|
|
||||||
OCIImage: plugin.Image,
|
|
||||||
Args: nil,
|
|
||||||
Sha256: shaBytes,
|
|
||||||
Builtin: false,
|
|
||||||
Runtime: ociRuntime,
|
|
||||||
RuntimeConfig: &pluginruntimeutil.PluginRuntimeConfig{
|
|
||||||
OCIRuntime: ociRuntime,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var version logical.PluginVersion
|
|
||||||
var err error
|
|
||||||
if plugin.Typ == consts.PluginTypeDatabase {
|
|
||||||
version, err = c.pluginCatalog.getDatabaseRunningVersion(context.Background(), entry)
|
|
||||||
} else {
|
|
||||||
version, err = c.pluginCatalog.getBackendRunningVersion(context.Background(), entry)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if version.Version != plugin.Version {
|
|
||||||
t.Errorf("Expected to get version %v but got %v", plugin.Version, version.Version)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerContainerPlugin(t *testing.T, sys *SystemBackend, pluginName, pluginType, version, sha, image, runtime string) {
|
func registerContainerPlugin(t *testing.T, sys *SystemBackend, pluginName, pluginType, version, sha, image, runtime string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("plugins/catalog/%s/%s", pluginType, pluginName))
|
req := logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("plugins/catalog/%s/%s", pluginType, pluginName))
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ package vault
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -19,10 +17,10 @@ import (
|
|||||||
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
||||||
"github.com/hashicorp/vault/sdk/framework"
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/hashicorp/vault/sdk/plugin"
|
"github.com/hashicorp/vault/sdk/plugin"
|
||||||
"github.com/hashicorp/vault/sdk/plugin/mock"
|
"github.com/hashicorp/vault/sdk/plugin/mock"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/hashicorp/vault/version"
|
"github.com/hashicorp/vault/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,8 +29,7 @@ const vaultTestingMockPluginEnv = "VAULT_TESTING_MOCK_PLUGIN"
|
|||||||
// version is used to override the plugin's self-reported version
|
// version is used to override the plugin's self-reported version
|
||||||
func testCoreWithPlugins(t *testing.T, typ consts.PluginType, versions ...string) (*Core, []pluginhelpers.TestPlugin) {
|
func testCoreWithPlugins(t *testing.T, typ consts.PluginType, versions ...string) (*Core, []pluginhelpers.TestPlugin) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
var plugins []pluginhelpers.TestPlugin
|
var plugins []pluginhelpers.TestPlugin
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
@@ -186,8 +183,7 @@ func TestCore_EnableExternalPlugin_MultipleVersions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) {
|
func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
// create an external plugin to shadow the builtin "pending-removal-test-plugin"
|
// create an external plugin to shadow the builtin "pending-removal-test-plugin"
|
||||||
pluginName := "therug"
|
pluginName := "therug"
|
||||||
@@ -207,7 +203,7 @@ func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) {
|
|||||||
// Register a plugin
|
// 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, "", "")
|
mountPlugin(t, c.systemBackend, pluginName, consts.PluginTypeCredential, "", "")
|
||||||
plugct := len(c.pluginCatalog.externalPlugins)
|
plugct := c.pluginCatalog.Processes()
|
||||||
if plugct != 1 {
|
if plugct != 1 {
|
||||||
t.Fatalf("expected a single external plugin entry after registering, got: %d", plugct)
|
t.Fatalf("expected a single external plugin entry after registering, got: %d", plugct)
|
||||||
}
|
}
|
||||||
@@ -228,7 +224,7 @@ func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugct = len(c.pluginCatalog.externalPlugins)
|
plugct = c.pluginCatalog.Processes()
|
||||||
if plugct != 0 {
|
if plugct != 0 {
|
||||||
t.Fatalf("expected no plugin entries after unseal, got: %d", plugct)
|
t.Fatalf("expected no plugin entries after unseal, got: %d", plugct)
|
||||||
}
|
}
|
||||||
@@ -258,8 +254,7 @@ func TestCore_EnableExternalPlugin_Deregister_SealUnseal(t *testing.T) {
|
|||||||
// version store is cleared. Vault sees the next unseal as a major upgrade and
|
// version store is cleared. Vault sees the next unseal as a major upgrade and
|
||||||
// should immediately shut down.
|
// should immediately shut down.
|
||||||
func TestCore_Unseal_isMajorVersionFirstMount_PendingRemoval_Plugin(t *testing.T) {
|
func TestCore_Unseal_isMajorVersionFirstMount_PendingRemoval_Plugin(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
// create an external plugin to shadow the builtin "pending-removal-test-plugin"
|
// create an external plugin to shadow the builtin "pending-removal-test-plugin"
|
||||||
pluginName := "pending-removal-test-plugin"
|
pluginName := "pending-removal-test-plugin"
|
||||||
@@ -337,8 +332,7 @@ func TestCore_Unseal_isMajorVersionFirstMount_PendingRemoval_Plugin(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCore_EnableExternalPlugin_PendingRemoval(t *testing.T) {
|
func TestCore_EnableExternalPlugin_PendingRemoval(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
// create an external plugin to shadow the builtin "pending-removal-test-plugin"
|
// create an external plugin to shadow the builtin "pending-removal-test-plugin"
|
||||||
pluginName := "pending-removal-test-plugin"
|
pluginName := "pending-removal-test-plugin"
|
||||||
@@ -372,8 +366,7 @@ func TestCore_EnableExternalPlugin_PendingRemoval(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCore_EnableExternalPlugin_ShadowBuiltin(t *testing.T) {
|
func TestCore_EnableExternalPlugin_ShadowBuiltin(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
// create an external plugin to shadow the builtin "approle"
|
// create an external plugin to shadow the builtin "approle"
|
||||||
plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir)
|
plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir)
|
||||||
@@ -451,8 +444,7 @@ func TestCore_EnableExternalPlugin_ShadowBuiltin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCore_EnableExternalKv_MultipleVersions(t *testing.T) {
|
func TestCore_EnableExternalKv_MultipleVersions(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
// new kv plugin can be registered but not mounted
|
// new kv plugin can be registered but not mounted
|
||||||
plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeSecrets, "v1.2.3", pluginDir)
|
plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeSecrets, "v1.2.3", pluginDir)
|
||||||
@@ -504,8 +496,7 @@ func TestCore_EnableExternalKv_MultipleVersions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCore_EnableExternalNoop_MultipleVersions(t *testing.T) {
|
func TestCore_EnableExternalNoop_MultipleVersions(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
|
|
||||||
// new noop plugin can be registered but not mounted
|
// new noop plugin can be registered but not mounted
|
||||||
plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir)
|
plugin := pluginhelpers.CompilePlugin(t, consts.PluginTypeCredential, "v1.2.3", pluginDir)
|
||||||
@@ -626,7 +617,7 @@ func TestCore_EnableExternalCredentialPlugin_NoVersionOnRegister(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
resp, _ := c.systemBackend.HandleRequest(namespace.RootContext(nil), req)
|
resp, _ := c.systemBackend.HandleRequest(namespace.RootContext(nil), req)
|
||||||
if resp == nil || !resp.IsError() || !strings.Contains(resp.Error().Error(), ErrPluginNotFound.Error()) {
|
if resp == nil || !resp.IsError() || !strings.Contains(resp.Error().Error(), plugincatalog.ErrPluginNotFound.Error()) {
|
||||||
t.Fatalf("Expected to get plugin not found but got: %v", resp.Error())
|
t.Fatalf("Expected to get plugin not found but got: %v", resp.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -663,55 +654,6 @@ func TestCore_EnableExternalCredentialPlugin_InvalidName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExternalPlugin_getBackendTypeVersion(t *testing.T) {
|
|
||||||
for name, tc := range map[string]struct {
|
|
||||||
pluginType consts.PluginType
|
|
||||||
setRunningVersion string
|
|
||||||
}{
|
|
||||||
"external credential plugin": {
|
|
||||||
pluginType: consts.PluginTypeCredential,
|
|
||||||
setRunningVersion: "v1.2.3",
|
|
||||||
},
|
|
||||||
"external secrets plugin": {
|
|
||||||
pluginType: consts.PluginTypeSecrets,
|
|
||||||
setRunningVersion: "v1.2.3",
|
|
||||||
},
|
|
||||||
"external database plugin": {
|
|
||||||
pluginType: consts.PluginTypeDatabase,
|
|
||||||
setRunningVersion: "v1.2.3",
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
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)
|
|
||||||
|
|
||||||
shaBytes, _ := hex.DecodeString(plugins[0].Sha256)
|
|
||||||
commandFull := filepath.Join(c.pluginCatalog.directory, plugins[0].FileName)
|
|
||||||
entry := &pluginutil.PluginRunner{
|
|
||||||
Name: plugins[0].Name,
|
|
||||||
Command: commandFull,
|
|
||||||
Args: nil,
|
|
||||||
Sha256: shaBytes,
|
|
||||||
Builtin: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
var version logical.PluginVersion
|
|
||||||
var err error
|
|
||||||
if tc.pluginType == consts.PluginTypeDatabase {
|
|
||||||
version, err = c.pluginCatalog.getDatabaseRunningVersion(context.Background(), entry)
|
|
||||||
} else {
|
|
||||||
version, err = c.pluginCatalog.getBackendRunningVersion(context.Background(), entry)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if version.Version != tc.setRunningVersion {
|
|
||||||
t.Errorf("Expected to get version %v but got %v", tc.setRunningVersion, version.Version)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExternalPlugin_CheckFilePermissions(t *testing.T) {
|
func TestExternalPlugin_CheckFilePermissions(t *testing.T) {
|
||||||
// Turn on the check.
|
// Turn on the check.
|
||||||
if err := os.Setenv(consts.VaultEnableFilePermissionsCheckEnv, "true"); err != nil {
|
if err := os.Setenv(consts.VaultEnableFilePermissionsCheckEnv, "true"); err != nil {
|
||||||
@@ -788,7 +730,10 @@ func TestExternalPlugin_CheckFilePermissions(t *testing.T) {
|
|||||||
|
|
||||||
func TestExternalPlugin_DifferentVersionsAndArgs_AreNotMultiplexed(t *testing.T) {
|
func TestExternalPlugin_DifferentVersionsAndArgs_AreNotMultiplexed(t *testing.T) {
|
||||||
env := []string{fmt.Sprintf("%s=yes", vaultTestingMockPluginEnv)}
|
env := []string{fmt.Sprintf("%s=yes", vaultTestingMockPluginEnv)}
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
|
core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
|
})
|
||||||
|
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
version string
|
version string
|
||||||
@@ -798,29 +743,32 @@ func TestExternalPlugin_DifferentVersionsAndArgs_AreNotMultiplexed(t *testing.T)
|
|||||||
{"v1.2.4", "TestBackend_PluginMain_Multiplexed_Logical_v124"},
|
{"v1.2.4", "TestBackend_PluginMain_Multiplexed_Logical_v124"},
|
||||||
} {
|
} {
|
||||||
// Register and mount plugins.
|
// Register and mount plugins.
|
||||||
TestAddTestPlugin(t, core, "mux-secret", consts.PluginTypeSecrets, tc.version, tc.testName, env, "")
|
TestAddTestPlugin(t, core, "mux-secret", consts.PluginTypeSecrets, tc.version, tc.testName, env)
|
||||||
mountPlugin(t, core.systemBackend, "mux-secret", consts.PluginTypeSecrets, tc.version, fmt.Sprintf("foo%d", i))
|
mountPlugin(t, core.systemBackend, "mux-secret", consts.PluginTypeSecrets, tc.version, fmt.Sprintf("foo%d", i))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(core.pluginCatalog.externalPlugins) != 2 {
|
if core.pluginCatalog.Processes() != 2 {
|
||||||
t.Fatalf("expected 2 external plugins, but got %d", len(core.pluginCatalog.externalPlugins))
|
t.Fatalf("expected 2 external plugins, but got %d", core.pluginCatalog.Processes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExternalPlugin_DifferentTypes_AreNotMultiplexed(t *testing.T) {
|
func TestExternalPlugin_DifferentTypes_AreNotMultiplexed(t *testing.T) {
|
||||||
const version = "v1.2.3"
|
const version = "v1.2.3"
|
||||||
env := []string{fmt.Sprintf("%s=yes", vaultTestingMockPluginEnv)}
|
env := []string{fmt.Sprintf("%s=yes", vaultTestingMockPluginEnv)}
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
|
core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
|
})
|
||||||
|
|
||||||
// Register and mount plugins.
|
// Register and mount plugins.
|
||||||
TestAddTestPlugin(t, core, "mux-aws", consts.PluginTypeSecrets, version, "TestBackend_PluginMain_Multiplexed_Logical_v123", env, "")
|
TestAddTestPlugin(t, core, "mux-aws", consts.PluginTypeSecrets, version, "TestBackend_PluginMain_Multiplexed_Logical_v123", env)
|
||||||
TestAddTestPlugin(t, core, "mux-aws", consts.PluginTypeCredential, version, "TestBackend_PluginMain_Multiplexed_Credential_v123", env, "")
|
TestAddTestPlugin(t, core, "mux-aws", consts.PluginTypeCredential, version, "TestBackend_PluginMain_Multiplexed_Credential_v123", env)
|
||||||
|
|
||||||
mountPlugin(t, core.systemBackend, "mux-aws", consts.PluginTypeSecrets, version, "")
|
mountPlugin(t, core.systemBackend, "mux-aws", consts.PluginTypeSecrets, version, "")
|
||||||
mountPlugin(t, core.systemBackend, "mux-aws", consts.PluginTypeCredential, version, "")
|
mountPlugin(t, core.systemBackend, "mux-aws", consts.PluginTypeCredential, version, "")
|
||||||
|
|
||||||
if len(core.pluginCatalog.externalPlugins) != 2 {
|
if core.pluginCatalog.Processes() != 2 {
|
||||||
t.Fatalf("expected 2 external plugins, but got %d", len(core.pluginCatalog.externalPlugins))
|
t.Fatalf("expected 2 external plugins, but got %d", core.pluginCatalog.Processes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,16 +782,19 @@ func TestExternalPlugin_DifferentEnv_AreNotMultiplexed(t *testing.T) {
|
|||||||
"FOO=BAR",
|
"FOO=BAR",
|
||||||
}
|
}
|
||||||
|
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
|
core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: pluginDir,
|
||||||
|
})
|
||||||
|
|
||||||
// Register and mount plugins.
|
// Register and mount plugins.
|
||||||
for i, env := range [][]string{baseEnv, alteredEnv} {
|
for i, env := range [][]string{baseEnv, alteredEnv} {
|
||||||
TestAddTestPlugin(t, core, "mux-secret", consts.PluginTypeSecrets, version, "TestBackend_PluginMain_Multiplexed_Logical_v123", env, "")
|
TestAddTestPlugin(t, core, "mux-secret", consts.PluginTypeSecrets, version, "TestBackend_PluginMain_Multiplexed_Logical_v123", env)
|
||||||
mountPlugin(t, core.systemBackend, "mux-secret", consts.PluginTypeSecrets, version, fmt.Sprintf("foo%d", i))
|
mountPlugin(t, core.systemBackend, "mux-secret", consts.PluginTypeSecrets, version, fmt.Sprintf("foo%d", i))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(core.pluginCatalog.externalPlugins) != 2 {
|
if core.pluginCatalog.Processes() != 2 {
|
||||||
t.Fatalf("expected 2 external plugins, but got %d", len(core.pluginCatalog.externalPlugins))
|
t.Fatalf("expected 2 external plugins, but got %d", core.pluginCatalog.Processes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getClusterWithFileAuditBackend(t *testing.T, typ consts.PluginType, numCores int) *vault.TestCluster {
|
func getClusterWithFileAuditBackend(t *testing.T, typ consts.PluginType, numCores int) *vault.TestCluster {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
PluginDirectory: pluginDir,
|
PluginDirectory: pluginDir,
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
@@ -63,8 +62,7 @@ func getClusterWithFileAuditBackend(t *testing.T, typ consts.PluginType, numCore
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCluster(t *testing.T, typ consts.PluginType, numCores int) *vault.TestCluster {
|
func getCluster(t *testing.T, typ consts.PluginType, numCores int) *vault.TestCluster {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
PluginDirectory: pluginDir,
|
PluginDirectory: pluginDir,
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
@@ -94,8 +92,7 @@ func getCluster(t *testing.T, typ consts.PluginType, numCores int) *vault.TestCl
|
|||||||
// rollback and reload a plugin without triggering race conditions by the go
|
// rollback and reload a plugin without triggering race conditions by the go
|
||||||
// race detector
|
// race detector
|
||||||
func TestExternalPlugin_RollbackAndReload(t *testing.T) {
|
func TestExternalPlugin_RollbackAndReload(t *testing.T) {
|
||||||
pluginDir, cleanup := corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
// set rollback period to a short interval to make conditions more "racy"
|
// set rollback period to a short interval to make conditions more "racy"
|
||||||
RollbackPeriod: 1 * time.Second,
|
RollbackPeriod: 1 * time.Second,
|
||||||
|
|||||||
@@ -6,15 +6,14 @@ package plugin_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/builtin/plugin"
|
"github.com/hashicorp/vault/builtin/plugin"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
vaulthttp "github.com/hashicorp/vault/http"
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
@@ -246,7 +245,7 @@ func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
|
|||||||
core := cluster.Cores[0]
|
core := cluster.Cores[0]
|
||||||
|
|
||||||
// Add a credential backend with the same name
|
// Add a credential backend with the same name
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "", "TestBackend_PluginMainCredentials", []string{}, "")
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "", "TestBackend_PluginMainCredentials", []string{})
|
||||||
|
|
||||||
// Make a request to lazy load the now-credential plugin
|
// Make a request to lazy load the now-credential plugin
|
||||||
// and expect an error
|
// and expect an error
|
||||||
@@ -256,9 +255,6 @@ func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("adding a same-named plugin of a different type should be no problem: %s", err)
|
t.Fatalf("adding a same-named plugin of a different type should be no problem: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep a bit before cleanup is called
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,13 +340,13 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
|
|||||||
switch btype {
|
switch btype {
|
||||||
case logical.TypeLogical:
|
case logical.TypeLogical:
|
||||||
// Add plugin back to the catalog
|
// Add plugin back to the catalog
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", logicalVersionMap[tc.pluginVersion], []string{}, "")
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", logicalVersionMap[tc.pluginVersion], []string{})
|
||||||
_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
|
_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
|
||||||
"type": "test",
|
"type": "test",
|
||||||
})
|
})
|
||||||
case logical.TypeCredential:
|
case logical.TypeCredential:
|
||||||
// Add plugin back to the catalog
|
// Add plugin back to the catalog
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "", credentialVersionMap[tc.pluginVersion], []string{}, "")
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "", credentialVersionMap[tc.pluginVersion], []string{})
|
||||||
_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
|
_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
|
||||||
"type": "test",
|
"type": "test",
|
||||||
})
|
})
|
||||||
@@ -588,6 +584,8 @@ func testSystemBackend_PluginReload(t *testing.T, reqData map[string]interface{}
|
|||||||
//
|
//
|
||||||
// The mounts are mounted at sys/mounts/mock-[numMounts] or sys/auth/mock-[numMounts]
|
// The mounts are mounted at sys/mounts/mock-[numMounts] or sys/auth/mock-[numMounts]
|
||||||
func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType logical.BackendType, pluginVersion string) *vault.TestCluster {
|
func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType logical.BackendType, pluginVersion string) *vault.TestCluster {
|
||||||
|
t.Helper()
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"plugin": plugin.Factory,
|
"plugin": plugin.Factory,
|
||||||
@@ -595,19 +593,14 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||||||
CredentialBackends: map[string]logical.Factory{
|
CredentialBackends: map[string]logical.Factory{
|
||||||
"plugin": plugin.Factory,
|
"plugin": plugin.Factory,
|
||||||
},
|
},
|
||||||
}
|
PluginDirectory: pluginDir,
|
||||||
|
|
||||||
// Create a tempdir, cluster.Cleanup will clean up this directory
|
|
||||||
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
HandlerFunc: vaulthttp.Handler,
|
HandlerFunc: vaulthttp.Handler,
|
||||||
KeepStandbysSealed: true,
|
KeepStandbysSealed: true,
|
||||||
NumCores: numCores,
|
NumCores: numCores,
|
||||||
TempDir: tempDir,
|
TempDir: pluginDir,
|
||||||
})
|
})
|
||||||
cluster.Start()
|
cluster.Start()
|
||||||
|
|
||||||
@@ -620,7 +613,7 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||||||
switch backendType {
|
switch backendType {
|
||||||
case logical.TypeLogical:
|
case logical.TypeLogical:
|
||||||
plugin := logicalVersionMap[pluginVersion]
|
plugin := logicalVersionMap[pluginVersion]
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", plugin, env, tempDir)
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", plugin, env)
|
||||||
for i := 0; i < numMounts; i++ {
|
for i := 0; i < numMounts; i++ {
|
||||||
// Alternate input styles for plugin_name on every other mount
|
// Alternate input styles for plugin_name on every other mount
|
||||||
options := map[string]interface{}{
|
options := map[string]interface{}{
|
||||||
@@ -636,7 +629,7 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||||||
}
|
}
|
||||||
case logical.TypeCredential:
|
case logical.TypeCredential:
|
||||||
plugin := credentialVersionMap[pluginVersion]
|
plugin := credentialVersionMap[pluginVersion]
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "", plugin, env, tempDir)
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeCredential, "", plugin, env)
|
||||||
for i := 0; i < numMounts; i++ {
|
for i := 0; i < numMounts; i++ {
|
||||||
// Alternate input styles for plugin_name on every other mount
|
// Alternate input styles for plugin_name on every other mount
|
||||||
options := map[string]interface{}{
|
options := map[string]interface{}{
|
||||||
@@ -666,22 +659,19 @@ func TestSystemBackend_Plugin_Env(t *testing.T) {
|
|||||||
// testSystemBackend_SingleCluster_Env is a helper func that returns a single
|
// testSystemBackend_SingleCluster_Env is a helper func that returns a single
|
||||||
// cluster and a single mounted plugin logical backend.
|
// cluster and a single mounted plugin logical backend.
|
||||||
func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.TestCluster {
|
func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.TestCluster {
|
||||||
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
coreConfig := &vault.CoreConfig{
|
coreConfig := &vault.CoreConfig{
|
||||||
LogicalBackends: map[string]logical.Factory{
|
LogicalBackends: map[string]logical.Factory{
|
||||||
"test": plugin.Factory,
|
"test": plugin.Factory,
|
||||||
},
|
},
|
||||||
}
|
PluginDirectory: pluginDir,
|
||||||
// Create a tempdir, cluster.Cleanup will clean up this directory
|
|
||||||
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
HandlerFunc: vaulthttp.Handler,
|
HandlerFunc: vaulthttp.Handler,
|
||||||
KeepStandbysSealed: true,
|
KeepStandbysSealed: true,
|
||||||
NumCores: 1,
|
NumCores: 1,
|
||||||
TempDir: tempDir,
|
TempDir: pluginDir,
|
||||||
})
|
})
|
||||||
cluster.Start()
|
cluster.Start()
|
||||||
|
|
||||||
@@ -690,7 +680,7 @@ func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.Test
|
|||||||
client := core.Client
|
client := core.Client
|
||||||
|
|
||||||
env = append([]string{pluginutil.PluginCACertPEMEnv + "=" + cluster.CACertPEMFile}, env...)
|
env = append([]string{pluginutil.PluginCACertPEMEnv + "=" + cluster.CACertPEMFile}, env...)
|
||||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", "TestBackend_PluginMainEnv", env, tempDir)
|
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", consts.PluginTypeSecrets, "", "TestBackend_PluginMainEnv", env)
|
||||||
options := map[string]interface{}{
|
options := map[string]interface{}{
|
||||||
"type": "mock-plugin",
|
"type": "mock-plugin",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/sdk/helper/roottoken"
|
"github.com/hashicorp/vault/sdk/helper/roottoken"
|
||||||
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/hashicorp/vault/version"
|
"github.com/hashicorp/vault/version"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
@@ -453,9 +454,6 @@ func (b *SystemBackend) handlePluginCatalogUntypedList(ctx context.Context, _ *l
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort for consistent ordering
|
|
||||||
sortVersionedPlugins(versioned)
|
|
||||||
|
|
||||||
versionedPlugins = append(versionedPlugins, versioned...)
|
versionedPlugins = append(versionedPlugins, versioned...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,23 +493,6 @@ func (b *SystemBackend) handlePluginCatalogUntypedList(ctx context.Context, _ *l
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortVersionedPlugins(versionedPlugins []pluginutil.VersionedPlugin) {
|
|
||||||
sort.SliceStable(versionedPlugins, func(i, j int) bool {
|
|
||||||
left, right := versionedPlugins[i], versionedPlugins[j]
|
|
||||||
if left.Type != right.Type {
|
|
||||||
return left.Type < right.Type
|
|
||||||
}
|
|
||||||
if left.Name != right.Name {
|
|
||||||
return left.Name < right.Name
|
|
||||||
}
|
|
||||||
if left.Version != right.Version {
|
|
||||||
return right.SemanticVersion.GreaterThan(left.SemanticVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, _ *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, _ *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
pluginName := d.Get("name").(string)
|
pluginName := d.Get("name").(string)
|
||||||
if pluginName == "" {
|
if pluginName == "" {
|
||||||
@@ -604,7 +585,7 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, _ *logica
|
|||||||
Sha256: sha256Bytes,
|
Sha256: sha256Bytes,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ErrPluginNotFound) || strings.HasPrefix(err.Error(), "plugin version mismatch") {
|
if errors.Is(err, plugincatalog.ErrPluginNotFound) || strings.HasPrefix(err.Error(), "plugin version mismatch") {
|
||||||
return logical.ErrorResponse(err.Error()), nil
|
return logical.ErrorResponse(err.Error()), nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -649,7 +630,7 @@ func (b *SystemBackend) handlePluginCatalogRead(ctx context.Context, _ *logical.
|
|||||||
|
|
||||||
command := plugin.Command
|
command := plugin.Command
|
||||||
if !plugin.Builtin && plugin.OCIImage == "" {
|
if !plugin.Builtin && plugin.OCIImage == "" {
|
||||||
command, err = filepath.Rel(b.Core.pluginCatalog.directory, command)
|
command, err = filepath.Rel(b.Core.pluginDirectory, command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -877,7 +858,7 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l
|
|||||||
}
|
}
|
||||||
|
|
||||||
conf, err := b.Core.pluginRuntimeCatalog.Get(ctx, runtimeName, runtimeType)
|
conf, err := b.Core.pluginRuntimeCatalog.Get(ctx, runtimeName, runtimeType)
|
||||||
if err != nil && !errors.Is(err, ErrPluginRuntimeNotFound) {
|
if err != nil && !errors.Is(err, plugincatalog.ErrPluginRuntimeNotFound) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import (
|
|||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||||
aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2"
|
aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2"
|
||||||
semver "github.com/hashicorp/go-version"
|
|
||||||
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
|
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
|
||||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||||
"github.com/hashicorp/vault/helper/experiments"
|
"github.com/hashicorp/vault/helper/experiments"
|
||||||
@@ -43,6 +42,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
|
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/hashicorp/vault/vault/seal"
|
"github.com/hashicorp/vault/vault/seal"
|
||||||
"github.com/hashicorp/vault/version"
|
"github.com/hashicorp/vault/version"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
@@ -2173,7 +2173,14 @@ func TestSystemBackend_disableAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_tuneAuth(t *testing.T) {
|
func TestSystemBackend_tuneAuth(t *testing.T) {
|
||||||
c, b, _ := testCoreSystemBackend(t)
|
tempDir, err := filepath.EvalSymlinks(os.TempDir())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
c, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: tempDir,
|
||||||
|
})
|
||||||
|
b := c.systemBackend
|
||||||
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
||||||
return &NoopBackend{BackendType: logical.TypeCredential}, nil
|
return &NoopBackend{BackendType: logical.TypeCredential}, nil
|
||||||
}
|
}
|
||||||
@@ -2188,7 +2195,7 @@ func TestSystemBackend_tuneAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -2209,24 +2216,19 @@ func TestSystemBackend_tuneAuth(t *testing.T) {
|
|||||||
req.Data["description"] = ""
|
req.Data["description"] = ""
|
||||||
req.Data["plugin_version"] = "v1.0.0"
|
req.Data["plugin_version"] = "v1.0.0"
|
||||||
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
||||||
if err == nil || resp == nil || !resp.IsError() || !strings.Contains(resp.Error().Error(), ErrPluginNotFound.Error()) {
|
if err == nil || resp == nil || !resp.IsError() || !strings.Contains(resp.Error().Error(), plugincatalog.ErrPluginNotFound.Error()) {
|
||||||
t.Fatalf("expected tune request to fail, but got resp: %#v, err: %s", resp, err)
|
t.Fatalf("expected tune request to fail, but got resp: %#v, err: %s", resp, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register the plugin in the catalog, and then try the same request again.
|
// Register the plugin in the catalog, and then try the same request again.
|
||||||
{
|
{
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
c.pluginCatalog.directory = tempDir
|
|
||||||
file, err := os.Create(filepath.Join(tempDir, "foo"))
|
file, err := os.Create(filepath.Join(tempDir, "foo"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -2263,7 +2265,7 @@ func TestSystemBackend_tuneAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -3323,13 +3325,14 @@ func testCoreSystemBackendRaw(t *testing.T) (*Core, logical.Backend, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
||||||
c, b, _ := testCoreSystemBackend(t)
|
|
||||||
// Bootstrap the pluginCatalog
|
|
||||||
sym, err := filepath.EvalSymlinks(os.TempDir())
|
sym, err := filepath.EvalSymlinks(os.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error: %v", err)
|
t.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
c.pluginCatalog.directory = sym
|
c, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: sym,
|
||||||
|
})
|
||||||
|
b := c.systemBackend
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.ListOperation, "plugins/catalog/database")
|
req := logical.TestRequest(t, logical.ListOperation, "plugins/catalog/database")
|
||||||
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
|
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
|
||||||
@@ -3339,7 +3342,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
|
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -3356,7 +3359,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
|
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -3401,7 +3404,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
|
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -3440,7 +3443,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
|
|
||||||
schema.ValidateResponse(
|
schema.ValidateResponse(
|
||||||
t,
|
t,
|
||||||
schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation),
|
schema.GetResponseSchema(t, b.Route(req.Path), req.Operation),
|
||||||
resp,
|
resp,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -3500,13 +3503,14 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||||||
// TestSystemBackend_PluginCatalog_ContainerCRUD tests that plugins registered
|
// TestSystemBackend_PluginCatalog_ContainerCRUD tests that plugins registered
|
||||||
// with oci_image set get recorded properly in the catalog.
|
// with oci_image set get recorded properly in the catalog.
|
||||||
func TestSystemBackend_PluginCatalog_ContainerCRUD(t *testing.T) {
|
func TestSystemBackend_PluginCatalog_ContainerCRUD(t *testing.T) {
|
||||||
c, b, _ := testCoreSystemBackend(t)
|
|
||||||
// Bootstrap the pluginCatalog
|
|
||||||
sym, err := filepath.EvalSymlinks(os.TempDir())
|
sym, err := filepath.EvalSymlinks(os.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error: %v", err)
|
t.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
c.pluginCatalog.directory = sym
|
c, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: sym,
|
||||||
|
})
|
||||||
|
b := c.systemBackend
|
||||||
|
|
||||||
for name, tc := range map[string]struct {
|
for name, tc := range map[string]struct {
|
||||||
in, expected map[string]any
|
in, expected map[string]any
|
||||||
@@ -3618,13 +3622,14 @@ func TestSystemBackend_PluginCatalog_ListPlugins_SucceedsWithAuditLogEnabled(t *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSystemBackend_PluginCatalog_CannotRegisterBuiltinPlugins(t *testing.T) {
|
func TestSystemBackend_PluginCatalog_CannotRegisterBuiltinPlugins(t *testing.T) {
|
||||||
c, b, _ := testCoreSystemBackend(t)
|
|
||||||
// Bootstrap the pluginCatalog
|
|
||||||
sym, err := filepath.EvalSymlinks(os.TempDir())
|
sym, err := filepath.EvalSymlinks(os.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error: %v", err)
|
t.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
c.pluginCatalog.directory = sym
|
c, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: sym,
|
||||||
|
})
|
||||||
|
b := c.systemBackend
|
||||||
|
|
||||||
// Set a plugin
|
// Set a plugin
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "plugins/catalog/database/test-plugin")
|
req := logical.TestRequest(t, logical.UpdateOperation, "plugins/catalog/database/test-plugin")
|
||||||
@@ -5816,79 +5821,6 @@ func TestSystemBackend_LoggersByName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSortVersionedPlugins(t *testing.T) {
|
|
||||||
versionedPlugin := func(typ consts.PluginType, name string, version string, builtin bool) pluginutil.VersionedPlugin {
|
|
||||||
return pluginutil.VersionedPlugin{
|
|
||||||
Type: typ.String(),
|
|
||||||
Name: name,
|
|
||||||
Version: version,
|
|
||||||
SHA256: "",
|
|
||||||
Builtin: builtin,
|
|
||||||
SemanticVersion: func() *semver.Version {
|
|
||||||
if version != "" {
|
|
||||||
return semver.Must(semver.NewVersion(version))
|
|
||||||
}
|
|
||||||
|
|
||||||
return semver.Must(semver.NewVersion("0.0.0"))
|
|
||||||
}(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
differingTypes := []pluginutil.VersionedPlugin{
|
|
||||||
versionedPlugin(consts.PluginTypeSecrets, "c", "1.0.0", false),
|
|
||||||
versionedPlugin(consts.PluginTypeDatabase, "c", "1.0.0", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", false),
|
|
||||||
}
|
|
||||||
differingNames := []pluginutil.VersionedPlugin{
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "b", "1.0.0", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "a", "1.0.0", false),
|
|
||||||
}
|
|
||||||
differingVersions := []pluginutil.VersionedPlugin{
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "10.0.0", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "2.0.1", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "2.1.0", false),
|
|
||||||
}
|
|
||||||
versionedUnversionedAndBuiltin := []pluginutil.VersionedPlugin{
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "", false),
|
|
||||||
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", true),
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range map[string][]pluginutil.VersionedPlugin{
|
|
||||||
"ascending types": differingTypes,
|
|
||||||
"ascending names": differingNames,
|
|
||||||
"ascending versions": differingVersions,
|
|
||||||
// Include differing versions twice so we can test out equality too.
|
|
||||||
"differing types, names and versions": append(differingTypes,
|
|
||||||
append(differingNames,
|
|
||||||
append(differingVersions, differingVersions...)...)...),
|
|
||||||
"mix of unversioned, versioned, and builtin": versionedUnversionedAndBuiltin,
|
|
||||||
} {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
sortVersionedPlugins(tc)
|
|
||||||
for i := 1; i < len(tc); i++ {
|
|
||||||
previous := tc[i-1]
|
|
||||||
current := tc[i]
|
|
||||||
if current.Type > previous.Type {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if current.Name > previous.Name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if current.SemanticVersion.GreaterThan(previous.SemanticVersion) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if current.Type == previous.Type && current.Name == previous.Name && current.SemanticVersion.Equal(previous.SemanticVersion) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatalf("versioned plugins at index %d and %d were not properly sorted: %+v, %+v", i-1, i, previous, current)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateVersion(t *testing.T) {
|
func TestValidateVersion(t *testing.T) {
|
||||||
b := testSystemBackend(t).(*SystemBackend)
|
b := testSystemBackend(t).(*SystemBackend)
|
||||||
k8sAuthBuiltin := versions.GetBuiltinVersion(consts.PluginTypeCredential, "kubernetes")
|
k8sAuthBuiltin := versions.GetBuiltinVersion(consts.PluginTypeCredential, "kubernetes")
|
||||||
@@ -5927,12 +5859,13 @@ func TestValidateVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateVersion_HelpfulErrorWhenBuiltinOverridden(t *testing.T) {
|
func TestValidateVersion_HelpfulErrorWhenBuiltinOverridden(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
tempDir, err := filepath.EvalSymlinks(os.TempDir())
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
core.pluginCatalog.directory = tempDir
|
core, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: tempDir,
|
||||||
|
})
|
||||||
b := core.systemBackend
|
b := core.systemBackend
|
||||||
|
|
||||||
// Shadow a builtin and test getting a helpful error back.
|
// Shadow a builtin and test getting a helpful error back.
|
||||||
@@ -6378,7 +6311,14 @@ func TestSystemBackend_pluginRuntime_CannotDeleteRuntimeWithReferencingPlugins(t
|
|||||||
if runtime.GOOS != "linux" {
|
if runtime.GOOS != "linux" {
|
||||||
t.Skip("Currently plugincontainer only supports linux")
|
t.Skip("Currently plugincontainer only supports linux")
|
||||||
}
|
}
|
||||||
c, b, _ := testCoreSystemBackend(t)
|
sym, err := filepath.EvalSymlinks(os.TempDir())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error: %v", err)
|
||||||
|
}
|
||||||
|
c, _, _ := TestCoreUnsealedWithConfig(t, &CoreConfig{
|
||||||
|
PluginDirectory: sym,
|
||||||
|
})
|
||||||
|
b := c.systemBackend
|
||||||
|
|
||||||
conf := pluginruntimeutil.PluginRuntimeConfig{
|
conf := pluginruntimeutil.PluginRuntimeConfig{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
@@ -6406,13 +6346,6 @@ func TestSystemBackend_pluginRuntime_CannotDeleteRuntimeWithReferencingPlugins(t
|
|||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap the pluginCatalog
|
|
||||||
sym, err := filepath.EvalSymlinks(os.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error: %v", err)
|
|
||||||
}
|
|
||||||
c.pluginCatalog.directory = sym
|
|
||||||
|
|
||||||
// Register the plugin referencing the runtime.
|
// Register the plugin referencing the runtime.
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, "plugins/catalog/database/test-plugin")
|
req = logical.TestRequest(t, logical.UpdateOperation, "plugins/catalog/database/test-plugin")
|
||||||
req.Data["version"] = "v0.16.0"
|
req.Data["version"] = "v0.16.0"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1690,7 +1691,7 @@ func (c *Core) newLogicalBackend(ctx context.Context, entry *MountEntry, sysView
|
|||||||
if entry.Version != "" {
|
if entry.Version != "" {
|
||||||
errContext += fmt.Sprintf(", version=%s", entry.Version)
|
errContext += fmt.Sprintf(", version=%s", entry.Version)
|
||||||
}
|
}
|
||||||
return nil, "", fmt.Errorf("%w: %s", ErrPluginNotFound, errContext)
|
return nil, "", fmt.Errorf("%w: %s", plugincatalog.ErrPluginNotFound, errContext)
|
||||||
}
|
}
|
||||||
if len(plug.Sha256) > 0 {
|
if len(plug.Sha256) > 0 {
|
||||||
runningSha = hex.EncodeToString(plug.Sha256)
|
runningSha = hex.EncodeToString(plug.Sha256)
|
||||||
|
|||||||
17
vault/plugincatalog/builtin_registry.go
Normal file
17
vault/plugincatalog/builtin_registry.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package plugincatalog
|
||||||
|
|
||||||
|
import "github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
|
|
||||||
|
// BuiltinRegistry is an interface that allows the "vault" package to use
|
||||||
|
// the registry of builtin plugins without getting an import cycle. It
|
||||||
|
// also allows for mocking the registry easily.
|
||||||
|
type BuiltinRegistry interface {
|
||||||
|
Contains(name string, pluginType consts.PluginType) bool
|
||||||
|
Get(name string, pluginType consts.PluginType) (func() (interface{}, error), bool)
|
||||||
|
Keys(pluginType consts.PluginType) []string
|
||||||
|
DeprecationStatus(name string, pluginType consts.PluginType) (consts.DeprecationStatus, bool)
|
||||||
|
IsBuiltinEntPlugin(name string, pluginType consts.PluginType) bool
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package vault
|
package plugincatalog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -33,7 +34,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
pluginCatalogPath = "core/plugin-catalog/"
|
|
||||||
ErrDirectoryNotConfigured = errors.New("could not set plugin, plugin directory is not configured")
|
ErrDirectoryNotConfigured = errors.New("could not set plugin, plugin directory is not configured")
|
||||||
ErrPluginNotFound = errors.New("plugin not found in the catalog")
|
ErrPluginNotFound = errors.New("plugin not found in the catalog")
|
||||||
ErrPluginConnectionNotFound = errors.New("plugin connection not found for client")
|
ErrPluginConnectionNotFound = errors.New("plugin connection not found for client")
|
||||||
@@ -45,7 +45,7 @@ var (
|
|||||||
// plugins are automatically detected and included in the catalog.
|
// plugins are automatically detected and included in the catalog.
|
||||||
type PluginCatalog struct {
|
type PluginCatalog struct {
|
||||||
builtinRegistry BuiltinRegistry
|
builtinRegistry BuiltinRegistry
|
||||||
catalogView *BarrierView
|
catalogView logical.Storage
|
||||||
directory string
|
directory string
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
@@ -137,67 +137,37 @@ type pluginClient struct {
|
|||||||
plugin.ClientProtocol
|
plugin.ClientProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapFactoryCheckPerms(core *Core, f logical.Factory) logical.Factory {
|
func SetupPluginCatalog(
|
||||||
return func(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
|
ctx context.Context,
|
||||||
pluginName := conf.Config["plugin_name"]
|
logger log.Logger,
|
||||||
pluginVersion := conf.Config["plugin_version"]
|
builtinRegistry BuiltinRegistry,
|
||||||
pluginTypeRaw := conf.Config["plugin_type"]
|
catalogView logical.Storage,
|
||||||
pluginType, err := consts.ParsePluginType(pluginTypeRaw)
|
pluginDirectory string,
|
||||||
if err != nil {
|
enableMlock bool,
|
||||||
return nil, err
|
pluginRuntimeCatalog *PluginRuntimeCatalog,
|
||||||
}
|
) (*PluginCatalog, error) {
|
||||||
|
pluginCatalog := &PluginCatalog{
|
||||||
pluginDescription := fmt.Sprintf("%s plugin %s", pluginTypeRaw, pluginName)
|
builtinRegistry: builtinRegistry,
|
||||||
if pluginVersion != "" {
|
catalogView: catalogView,
|
||||||
pluginDescription += " version " + pluginVersion
|
directory: pluginDirectory,
|
||||||
}
|
logger: logger,
|
||||||
|
mlockPlugins: enableMlock,
|
||||||
plugin, err := core.pluginCatalog.Get(ctx, pluginName, pluginType, pluginVersion)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to find %s in plugin catalog: %w", pluginDescription, err)
|
|
||||||
}
|
|
||||||
if plugin == nil {
|
|
||||||
return nil, fmt.Errorf("failed to find %s in plugin catalog", pluginDescription)
|
|
||||||
}
|
|
||||||
if plugin.OCIImage != "" {
|
|
||||||
return f(ctx, conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
command, err := filepath.Rel(core.pluginCatalog.directory, plugin.Command)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to compute plugin command: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := core.CheckPluginPerms(command); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return f(ctx, conf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) setupPluginCatalog(ctx context.Context) error {
|
|
||||||
c.pluginCatalog = &PluginCatalog{
|
|
||||||
builtinRegistry: c.builtinRegistry,
|
|
||||||
catalogView: NewBarrierView(c.barrier, pluginCatalogPath),
|
|
||||||
directory: c.pluginDirectory,
|
|
||||||
logger: c.logger,
|
|
||||||
mlockPlugins: c.enableMlock,
|
|
||||||
wrapper: logical.StaticSystemView{VersionString: version.GetVersion().Version},
|
wrapper: logical.StaticSystemView{VersionString: version.GetVersion().Version},
|
||||||
runtimeCatalog: c.pluginRuntimeCatalog,
|
runtimeCatalog: pluginRuntimeCatalog,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run upgrade if untyped plugins exist
|
// Run upgrade if untyped plugins exist
|
||||||
err := c.pluginCatalog.UpgradePlugins(ctx, c.logger)
|
err := pluginCatalog.UpgradePlugins(ctx, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("error while upgrading plugin storage", "error", err)
|
logger.Error("error while upgrading plugin storage", "error", err)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.logger.IsInfo() {
|
if logger.IsInfo() {
|
||||||
c.logger.Info("successfully setup plugin catalog", "plugin-directory", c.pluginDirectory)
|
logger.Info("successfully setup plugin catalog", "plugin-directory", pluginDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return pluginCatalog, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type pluginClientConn struct {
|
type pluginClientConn struct {
|
||||||
@@ -232,6 +202,10 @@ func (p *pluginClient) Reload() error {
|
|||||||
return p.reloadFunc()
|
return p.reloadFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *PluginCatalog) Processes() int {
|
||||||
|
return len(c.externalPlugins)
|
||||||
|
}
|
||||||
|
|
||||||
// reloadExternalPlugin
|
// reloadExternalPlugin
|
||||||
// This should be called with the write lock held.
|
// This should be called with the write lock held.
|
||||||
func (c *PluginCatalog) reloadExternalPlugin(key externalPluginsKey, id, pluginBinaryRef string) error {
|
func (c *PluginCatalog) reloadExternalPlugin(key externalPluginsKey, id, pluginBinaryRef string) error {
|
||||||
@@ -781,6 +755,11 @@ func (c *PluginCatalog) UpgradePlugins(ctx context.Context, logger log.Logger) e
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pluginRaw == nil {
|
||||||
|
retErr = multierror.Append(fmt.Errorf("%q plugin entry was nil", pluginName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
plugin := new(pluginutil.PluginRunner)
|
plugin := new(pluginutil.PluginRunner)
|
||||||
if err := jsonutil.DecodeJSON(pluginRaw.Value, plugin); err != nil {
|
if err := jsonutil.DecodeJSON(pluginRaw.Value, plugin); err != nil {
|
||||||
retErr = multierror.Append(fmt.Errorf("failed to decode plugin entry: %w", err))
|
retErr = multierror.Append(fmt.Errorf("failed to decode plugin entry: %w", err))
|
||||||
@@ -1187,5 +1166,25 @@ func (c *PluginCatalog) listInternal(ctx context.Context, pluginType consts.Plug
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the result for consistent ordering.
|
||||||
|
sortVersionedPlugins(result)
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sortVersionedPlugins(versionedPlugins []pluginutil.VersionedPlugin) {
|
||||||
|
sort.SliceStable(versionedPlugins, func(i, j int) bool {
|
||||||
|
left, right := versionedPlugins[i], versionedPlugins[j]
|
||||||
|
if left.Type != right.Type {
|
||||||
|
return left.Type < right.Type
|
||||||
|
}
|
||||||
|
if left.Name != right.Name {
|
||||||
|
return left.Name < right.Name
|
||||||
|
}
|
||||||
|
if left.Version != right.Version {
|
||||||
|
return right.SemanticVersion.GreaterThan(left.SemanticVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,52 +1,85 @@
|
|||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package vault
|
package plugincatalog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
|
log "github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/hashicorp/go-version"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/builtin/credential/userpass"
|
"github.com/hashicorp/vault/builtin/credential/userpass"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
||||||
"github.com/hashicorp/vault/helper/versions"
|
"github.com/hashicorp/vault/helper/versions"
|
||||||
"github.com/hashicorp/vault/plugins/database/postgresql"
|
"github.com/hashicorp/vault/plugins/database/postgresql"
|
||||||
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginruntimeutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginruntimeutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/sdk/physical/inmem"
|
||||||
backendplugin "github.com/hashicorp/vault/sdk/plugin"
|
backendplugin "github.com/hashicorp/vault/sdk/plugin"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPluginCatalog_CRUD(t *testing.T) {
|
func testPluginCatalog(t *testing.T) *PluginCatalog {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
logger := hclog.New(&hclog.LoggerOptions{
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
Level: hclog.Trace,
|
||||||
|
})
|
||||||
|
storage, err := inmem.NewInmem(nil, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
core.pluginCatalog.directory = tempDir
|
testDir, err := filepath.EvalSymlinks(filepath.Dir(os.Args[0]))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pluginRuntimeCatalog := testPluginRuntimeCatalog(t)
|
||||||
|
pluginCatalog, err := SetupPluginCatalog(
|
||||||
|
context.Background(),
|
||||||
|
logger,
|
||||||
|
corehelpers.NewMockBuiltinRegistry(),
|
||||||
|
logical.NewLogicalStorage(storage),
|
||||||
|
testDir,
|
||||||
|
false,
|
||||||
|
pluginRuntimeCatalog,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return pluginCatalog
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPluginCatalog_CRUD(t *testing.T) {
|
||||||
const pluginName = "mysql-database-plugin"
|
const pluginName = "mysql-database-plugin"
|
||||||
|
|
||||||
|
pluginCatalog := testPluginCatalog(t)
|
||||||
|
|
||||||
// Get builtin plugin
|
// Get builtin plugin
|
||||||
p, err := core.pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
p, err := pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get it again, explicitly specifying builtin version
|
// Get it again, explicitly specifying builtin version
|
||||||
builtinVersion := versions.GetBuiltinVersion(consts.PluginTypeDatabase, pluginName)
|
builtinVersion := versions.GetBuiltinVersion(consts.PluginTypeDatabase, pluginName)
|
||||||
p2, err := core.pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, builtinVersion)
|
p2, err := pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, builtinVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -73,14 +106,14 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a plugin, test overwriting a builtin plugin
|
// Set a plugin, test overwriting a builtin plugin
|
||||||
file, err := os.CreateTemp(tempDir, "temp")
|
file, err := os.CreateTemp(pluginCatalog.directory, "temp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
command := filepath.Base(file.Name())
|
command := filepath.Base(file.Name())
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: pluginName,
|
Name: pluginName,
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: "",
|
Version: "",
|
||||||
@@ -94,14 +127,14 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the plugin
|
// Get the plugin
|
||||||
p, err = core.pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
p, err = pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get it again, explicitly specifying builtin version.
|
// Get it again, explicitly specifying builtin version.
|
||||||
// This time it should fail because it was overwritten.
|
// This time it should fail because it was overwritten.
|
||||||
p2, err = core.pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, builtinVersion)
|
p2, err = pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, builtinVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -112,7 +145,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||||||
expected := &pluginutil.PluginRunner{
|
expected := &pluginutil.PluginRunner{
|
||||||
Name: pluginName,
|
Name: pluginName,
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Command: filepath.Join(tempDir, filepath.Base(file.Name())),
|
Command: filepath.Join(pluginCatalog.directory, filepath.Base(file.Name())),
|
||||||
Args: []string{"--test"},
|
Args: []string{"--test"},
|
||||||
Env: []string{"FOO=BAR"},
|
Env: []string{"FOO=BAR"},
|
||||||
Sha256: []byte{'1'},
|
Sha256: []byte{'1'},
|
||||||
@@ -125,13 +158,13 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete the plugin
|
// Delete the plugin
|
||||||
err = core.pluginCatalog.Delete(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
err = pluginCatalog.Delete(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get builtin plugin
|
// Get builtin plugin
|
||||||
p, err = core.pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
p, err = pluginCatalog.Get(context.Background(), pluginName, consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -155,15 +188,10 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginCatalog := testPluginCatalog(t)
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
core.pluginCatalog.directory = tempDir
|
|
||||||
|
|
||||||
// Set a versioned plugin.
|
// Set a versioned plugin.
|
||||||
file, err := ioutil.TempFile(tempDir, "temp")
|
file, err := os.CreateTemp(pluginCatalog.directory, "temp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -172,7 +200,7 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
|||||||
const name = "mysql-database-plugin"
|
const name = "mysql-database-plugin"
|
||||||
const version = "1.0.0"
|
const version = "1.0.0"
|
||||||
command := fmt.Sprintf("%s", filepath.Base(file.Name()))
|
command := fmt.Sprintf("%s", filepath.Base(file.Name()))
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: version,
|
Version: version,
|
||||||
@@ -186,7 +214,7 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the plugin
|
// Get the plugin
|
||||||
plugin, err := core.pluginCatalog.Get(context.Background(), name, consts.PluginTypeDatabase, version)
|
plugin, err := pluginCatalog.Get(context.Background(), name, consts.PluginTypeDatabase, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -195,7 +223,7 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
|||||||
Name: name,
|
Name: name,
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: version,
|
Version: version,
|
||||||
Command: filepath.Join(tempDir, filepath.Base(file.Name())),
|
Command: filepath.Join(pluginCatalog.directory, filepath.Base(file.Name())),
|
||||||
Args: []string{"--test"},
|
Args: []string{"--test"},
|
||||||
Env: []string{"FOO=BAR"},
|
Env: []string{"FOO=BAR"},
|
||||||
Sha256: []byte{'1'},
|
Sha256: []byte{'1'},
|
||||||
@@ -208,7 +236,7 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
|||||||
|
|
||||||
// Also get the builtin version to check we can still access that.
|
// Also get the builtin version to check we can still access that.
|
||||||
builtinVersion := versions.GetBuiltinVersion(consts.PluginTypeDatabase, name)
|
builtinVersion := versions.GetBuiltinVersion(consts.PluginTypeDatabase, name)
|
||||||
plugin, err = core.pluginCatalog.Get(context.Background(), name, consts.PluginTypeDatabase, builtinVersion)
|
plugin, err = pluginCatalog.Get(context.Background(), name, consts.PluginTypeDatabase, builtinVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -237,13 +265,13 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete the plugin
|
// Delete the plugin
|
||||||
err = core.pluginCatalog.Delete(context.Background(), name, consts.PluginTypeDatabase, version)
|
err = pluginCatalog.Delete(context.Background(), name, consts.PluginTypeDatabase, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get plugin - should fail
|
// Get plugin - should fail
|
||||||
plugin, err = core.pluginCatalog.Get(context.Background(), name, consts.PluginTypeDatabase, version)
|
plugin, err = pluginCatalog.Get(context.Background(), name, consts.PluginTypeDatabase, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -253,19 +281,14 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginCatalog_List(t *testing.T) {
|
func TestPluginCatalog_List(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginCatalog := testPluginCatalog(t)
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
core.pluginCatalog.directory = tempDir
|
|
||||||
|
|
||||||
// Get builtin plugins and sort them
|
// Get builtin plugins and sort them
|
||||||
builtinKeys := builtinplugins.Registry.Keys(consts.PluginTypeDatabase)
|
builtinKeys := builtinplugins.Registry.Keys(consts.PluginTypeDatabase)
|
||||||
sort.Strings(builtinKeys)
|
sort.Strings(builtinKeys)
|
||||||
|
|
||||||
// List only builtin plugins
|
// List only builtin plugins
|
||||||
plugins, err := core.pluginCatalog.List(context.Background(), consts.PluginTypeDatabase)
|
plugins, err := pluginCatalog.List(context.Background(), consts.PluginTypeDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -280,14 +303,14 @@ func TestPluginCatalog_List(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a plugin, test overwriting a builtin plugin
|
// Set a plugin, test overwriting a builtin plugin
|
||||||
file, err := ioutil.TempFile(tempDir, "temp")
|
file, err := os.CreateTemp(pluginCatalog.directory, "temp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
command := filepath.Base(file.Name())
|
command := filepath.Base(file.Name())
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "mysql-database-plugin",
|
Name: "mysql-database-plugin",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: "",
|
Version: "",
|
||||||
@@ -301,7 +324,7 @@ func TestPluginCatalog_List(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set another plugin
|
// Set another plugin
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "aaaaaaa",
|
Name: "aaaaaaa",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: "",
|
Version: "",
|
||||||
@@ -315,7 +338,7 @@ func TestPluginCatalog_List(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List the plugins
|
// List the plugins
|
||||||
plugins, err = core.pluginCatalog.List(context.Background(), consts.PluginTypeDatabase)
|
plugins, err = pluginCatalog.List(context.Background(), consts.PluginTypeDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -338,19 +361,14 @@ func TestPluginCatalog_List(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginCatalog_ListVersionedPlugins(t *testing.T) {
|
func TestPluginCatalog_ListVersionedPlugins(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginCatalog := testPluginCatalog(t)
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
core.pluginCatalog.directory = tempDir
|
|
||||||
|
|
||||||
// Get builtin plugins and sort them
|
// Get builtin plugins and sort them
|
||||||
builtinKeys := builtinplugins.Registry.Keys(consts.PluginTypeDatabase)
|
builtinKeys := builtinplugins.Registry.Keys(consts.PluginTypeDatabase)
|
||||||
sort.Strings(builtinKeys)
|
sort.Strings(builtinKeys)
|
||||||
|
|
||||||
// List only builtin plugins
|
// List only builtin plugins
|
||||||
plugins, err := core.pluginCatalog.ListVersionedPlugins(context.Background(), consts.PluginTypeDatabase)
|
plugins, err := pluginCatalog.ListVersionedPlugins(context.Background(), consts.PluginTypeDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -367,14 +385,14 @@ func TestPluginCatalog_ListVersionedPlugins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a plugin, test overwriting a builtin plugin
|
// Set a plugin, test overwriting a builtin plugin
|
||||||
file, err := ioutil.TempFile(tempDir, "temp")
|
file, err := ioutil.TempFile(pluginCatalog.directory, "temp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
command := filepath.Base(file.Name())
|
command := filepath.Base(file.Name())
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "mysql-database-plugin",
|
Name: "mysql-database-plugin",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: "",
|
Version: "",
|
||||||
@@ -388,7 +406,7 @@ func TestPluginCatalog_ListVersionedPlugins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set another plugin, with version information
|
// Set another plugin, with version information
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "aaaaaaa",
|
Name: "aaaaaaa",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
Version: "1.1.0",
|
Version: "1.1.0",
|
||||||
@@ -402,7 +420,7 @@ func TestPluginCatalog_ListVersionedPlugins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List the plugins
|
// List the plugins
|
||||||
plugins, err = core.pluginCatalog.ListVersionedPlugins(context.Background(), consts.PluginTypeDatabase)
|
plugins, err = pluginCatalog.ListVersionedPlugins(context.Background(), consts.PluginTypeDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -450,14 +468,9 @@ func TestPluginCatalog_ListVersionedPlugins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginCatalog_ListHandlesPluginNamesWithSlashes(t *testing.T) {
|
func TestPluginCatalog_ListHandlesPluginNamesWithSlashes(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginCatalog := testPluginCatalog(t)
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
core.pluginCatalog.directory = tempDir
|
|
||||||
|
|
||||||
file, err := ioutil.TempFile(tempDir, "temp")
|
file, err := os.CreateTemp(pluginCatalog.directory, "temp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -489,7 +502,7 @@ func TestPluginCatalog_ListHandlesPluginNamesWithSlashes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, entry := range pluginsToRegister {
|
for _, entry := range pluginsToRegister {
|
||||||
err = core.pluginCatalog.Set(ctx, pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(ctx, pluginutil.SetPluginInput{
|
||||||
Name: entry.Name,
|
Name: entry.Name,
|
||||||
Type: consts.PluginTypeCredential,
|
Type: consts.PluginTypeCredential,
|
||||||
Version: entry.Version,
|
Version: entry.Version,
|
||||||
@@ -503,7 +516,7 @@ func TestPluginCatalog_ListHandlesPluginNamesWithSlashes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins, err := core.pluginCatalog.ListVersionedPlugins(ctx, consts.PluginTypeCredential)
|
plugins, err := pluginCatalog.ListVersionedPlugins(ctx, consts.PluginTypeCredential)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -524,30 +537,25 @@ func TestPluginCatalog_ListHandlesPluginNamesWithSlashes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginCatalog_NewPluginClient(t *testing.T) {
|
func TestPluginCatalog_NewPluginClient(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginCatalog := testPluginCatalog(t)
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
core.pluginCatalog.directory = tempDir
|
|
||||||
|
|
||||||
if extPlugins := len(core.pluginCatalog.externalPlugins); extPlugins != 0 {
|
if extPlugins := len(pluginCatalog.externalPlugins); extPlugins != 0 {
|
||||||
t.Fatalf("expected externalPlugins map to be of len 0 but got %d", extPlugins)
|
t.Fatalf("expected externalPlugins map to be of len 0 but got %d", extPlugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
// register plugins
|
// register plugins
|
||||||
TestAddTestPlugin(t, core, "mux-postgres", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_PostgresMultiplexed", []string{}, "")
|
TestAddTestPlugin(t, pluginCatalog, "mux-postgres", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_PostgresMultiplexed", []string{})
|
||||||
TestAddTestPlugin(t, core, "single-postgres-1", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Postgres", []string{}, "")
|
TestAddTestPlugin(t, pluginCatalog, "single-postgres-1", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Postgres", []string{})
|
||||||
TestAddTestPlugin(t, core, "single-postgres-2", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Postgres", []string{}, "")
|
TestAddTestPlugin(t, pluginCatalog, "single-postgres-2", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Postgres", []string{})
|
||||||
|
|
||||||
TestAddTestPlugin(t, core, "mux-userpass", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_UserpassMultiplexed", []string{}, "")
|
TestAddTestPlugin(t, pluginCatalog, "mux-userpass", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_UserpassMultiplexed", []string{})
|
||||||
TestAddTestPlugin(t, core, "single-userpass-1", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Userpass", []string{}, "")
|
TestAddTestPlugin(t, pluginCatalog, "single-userpass-1", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Userpass", []string{})
|
||||||
TestAddTestPlugin(t, core, "single-userpass-2", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Userpass", []string{}, "")
|
TestAddTestPlugin(t, pluginCatalog, "single-userpass-2", consts.PluginTypeUnknown, "", "TestPluginCatalog_PluginMain_Userpass", []string{})
|
||||||
|
|
||||||
getKey := func(pluginName string, pluginType consts.PluginType) externalPluginsKey {
|
getKey := func(pluginName string, pluginType consts.PluginType) externalPluginsKey {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
plugin, err := core.pluginCatalog.Get(ctx, pluginName, pluginType, "")
|
plugin, err := pluginCatalog.Get(ctx, pluginName, pluginType, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -565,27 +573,27 @@ func TestPluginCatalog_NewPluginClient(t *testing.T) {
|
|||||||
// run plugins
|
// run plugins
|
||||||
// run "mux-postgres" twice which will start a single plugin for 2
|
// run "mux-postgres" twice which will start a single plugin for 2
|
||||||
// distinct connections
|
// distinct connections
|
||||||
c := TestRunTestPlugin(t, core, consts.PluginTypeDatabase, "mux-postgres")
|
c := testRunTestPlugin(t, pluginCatalog, consts.PluginTypeDatabase, "mux-postgres")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeDatabase, "mux-postgres")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeDatabase, "mux-postgres")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeDatabase, "single-postgres-1")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeDatabase, "single-postgres-1")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeDatabase, "single-postgres-2")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeDatabase, "single-postgres-2")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
|
|
||||||
// run "mux-userpass" twice which will start a single plugin for 2
|
// run "mux-userpass" twice which will start a single plugin for 2
|
||||||
// distinct connections
|
// distinct connections
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeCredential, "mux-userpass")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeCredential, "mux-userpass")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeCredential, "mux-userpass")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeCredential, "mux-userpass")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeCredential, "single-userpass-1")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeCredential, "single-userpass-1")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
c = TestRunTestPlugin(t, core, consts.PluginTypeCredential, "single-userpass-2")
|
c = testRunTestPlugin(t, pluginCatalog, consts.PluginTypeCredential, "single-userpass-2")
|
||||||
pluginClients = append(pluginClients, c)
|
pluginClients = append(pluginClients, c)
|
||||||
|
|
||||||
externalPlugins := core.pluginCatalog.externalPlugins
|
externalPlugins := pluginCatalog.externalPlugins
|
||||||
if len(externalPlugins) != 6 {
|
if len(externalPlugins) != 6 {
|
||||||
t.Fatalf("expected externalPlugins map to be of len 6 but got %d", len(externalPlugins))
|
t.Fatalf("expected externalPlugins map to be of len 6 but got %d", len(externalPlugins))
|
||||||
}
|
}
|
||||||
@@ -654,13 +662,10 @@ func TestPluginCatalog_MakeExternalPluginsKey_Comparable(t *testing.T) {
|
|||||||
// always allow registration of container plugins (rejecting on non-Linux happens
|
// always allow registration of container plugins (rejecting on non-Linux happens
|
||||||
// in the logical system API handler).
|
// in the logical system API handler).
|
||||||
func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
catalog := testPluginCatalog(t)
|
||||||
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
tempDir := catalog.directory
|
||||||
if err != nil {
|
catalog.directory = ""
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
catalog := core.pluginCatalog
|
|
||||||
tests := map[string]func(t *testing.T){
|
tests := map[string]func(t *testing.T){
|
||||||
"set binary plugin": func(t *testing.T) {
|
"set binary plugin": func(t *testing.T) {
|
||||||
file, err := os.CreateTemp(tempDir, "temp")
|
file, err := os.CreateTemp(tempDir, "temp")
|
||||||
@@ -694,7 +699,7 @@ func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
|||||||
t.Fatal("expected error without directory set")
|
t.Fatal("expected error without directory set")
|
||||||
}
|
}
|
||||||
// Make sure we can still get builtins too
|
// Make sure we can still get builtins too
|
||||||
_, err = core.pluginCatalog.Get(context.Background(), "mysql-database-plugin", consts.PluginTypeDatabase, "")
|
_, err = catalog.Get(context.Background(), "mysql-database-plugin", consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -702,7 +707,7 @@ func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
|||||||
"set container plugin": func(t *testing.T) {
|
"set container plugin": func(t *testing.T) {
|
||||||
// Should never error.
|
// Should never error.
|
||||||
const image = "does-not-exist"
|
const image = "does-not-exist"
|
||||||
err = catalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err := catalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "container",
|
Name: "container",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
OCIImage: image,
|
OCIImage: image,
|
||||||
@@ -719,7 +724,7 @@ func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
|||||||
t.Fatalf("Expected %s, got %s", image, p.OCIImage)
|
t.Fatalf("Expected %s, got %s", image, p.OCIImage)
|
||||||
}
|
}
|
||||||
// Make sure we can still get builtins too
|
// Make sure we can still get builtins too
|
||||||
_, err = core.pluginCatalog.Get(context.Background(), "mysql-database-plugin", consts.PluginTypeDatabase, "")
|
_, err = catalog.Get(context.Background(), "mysql-database-plugin", consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error %v", err)
|
t.Fatalf("unexpected error %v", err)
|
||||||
}
|
}
|
||||||
@@ -732,7 +737,7 @@ func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
core.pluginCatalog.directory = tempDir
|
catalog.directory = tempDir
|
||||||
|
|
||||||
t.Run("directory set", func(t *testing.T) {
|
t.Run("directory set", func(t *testing.T) {
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
@@ -745,10 +750,10 @@ func TestPluginCatalog_ErrDirectoryNotConfigured(t *testing.T) {
|
|||||||
// are returned with their container runtime config populated if it was
|
// are returned with their container runtime config populated if it was
|
||||||
// specified.
|
// specified.
|
||||||
func TestRuntimeConfigPopulatedIfSpecified(t *testing.T) {
|
func TestRuntimeConfigPopulatedIfSpecified(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginCatalog := testPluginCatalog(t)
|
||||||
const image = "does-not-exist"
|
const image = "does-not-exist"
|
||||||
const runtime = "custom-runtime"
|
const runtime = "custom-runtime"
|
||||||
err := core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err := pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "container",
|
Name: "container",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
OCIImage: image,
|
OCIImage: image,
|
||||||
@@ -759,7 +764,7 @@ func TestRuntimeConfigPopulatedIfSpecified(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ociRuntime = "some-other-oci-runtime"
|
const ociRuntime = "some-other-oci-runtime"
|
||||||
err = core.pluginRuntimeCatalog.Set(context.Background(), &pluginruntimeutil.PluginRuntimeConfig{
|
err = pluginCatalog.runtimeCatalog.Set(context.Background(), &pluginruntimeutil.PluginRuntimeConfig{
|
||||||
Name: runtime,
|
Name: runtime,
|
||||||
Type: consts.PluginRuntimeTypeContainer,
|
Type: consts.PluginRuntimeTypeContainer,
|
||||||
OCIRuntime: ociRuntime,
|
OCIRuntime: ociRuntime,
|
||||||
@@ -769,7 +774,7 @@ func TestRuntimeConfigPopulatedIfSpecified(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now setting the plugin with a runtime should succeed.
|
// Now setting the plugin with a runtime should succeed.
|
||||||
err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
Name: "container",
|
Name: "container",
|
||||||
Type: consts.PluginTypeDatabase,
|
Type: consts.PluginTypeDatabase,
|
||||||
OCIImage: image,
|
OCIImage: image,
|
||||||
@@ -779,7 +784,7 @@ func TestRuntimeConfigPopulatedIfSpecified(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := core.pluginCatalog.Get(context.Background(), "container", consts.PluginTypeDatabase, "")
|
p, err := pluginCatalog.Get(context.Background(), "container", consts.PluginTypeDatabase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -874,3 +879,233 @@ func expectMultiplexingSupport(t *testing.T, expected, actual bool) {
|
|||||||
t.Fatalf("expected external plugin multiplexing support to be %t", expected)
|
t.Fatalf("expected external plugin multiplexing support to be %t", expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSortVersionedPlugins(t *testing.T) {
|
||||||
|
versionedPlugin := func(typ consts.PluginType, name string, pluginVersion string, builtin bool) pluginutil.VersionedPlugin {
|
||||||
|
return pluginutil.VersionedPlugin{
|
||||||
|
Type: typ.String(),
|
||||||
|
Name: name,
|
||||||
|
Version: pluginVersion,
|
||||||
|
SHA256: "",
|
||||||
|
Builtin: builtin,
|
||||||
|
SemanticVersion: func() *version.Version {
|
||||||
|
if pluginVersion != "" {
|
||||||
|
return version.Must(version.NewVersion(pluginVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
return version.Must(version.NewVersion("0.0.0"))
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
differingTypes := []pluginutil.VersionedPlugin{
|
||||||
|
versionedPlugin(consts.PluginTypeSecrets, "c", "1.0.0", false),
|
||||||
|
versionedPlugin(consts.PluginTypeDatabase, "c", "1.0.0", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", false),
|
||||||
|
}
|
||||||
|
differingNames := []pluginutil.VersionedPlugin{
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "b", "1.0.0", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "a", "1.0.0", false),
|
||||||
|
}
|
||||||
|
differingVersions := []pluginutil.VersionedPlugin{
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "10.0.0", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "2.0.1", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "2.1.0", false),
|
||||||
|
}
|
||||||
|
versionedUnversionedAndBuiltin := []pluginutil.VersionedPlugin{
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "", false),
|
||||||
|
versionedPlugin(consts.PluginTypeCredential, "c", "1.0.0", true),
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range map[string][]pluginutil.VersionedPlugin{
|
||||||
|
"ascending types": differingTypes,
|
||||||
|
"ascending names": differingNames,
|
||||||
|
"ascending versions": differingVersions,
|
||||||
|
// Include differing versions twice so we can test out equality too.
|
||||||
|
"differing types, names and versions": append(differingTypes,
|
||||||
|
append(differingNames,
|
||||||
|
append(differingVersions, differingVersions...)...)...),
|
||||||
|
"mix of unversioned, versioned, and builtin": versionedUnversionedAndBuiltin,
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
sortVersionedPlugins(tc)
|
||||||
|
for i := 1; i < len(tc); i++ {
|
||||||
|
previous := tc[i-1]
|
||||||
|
current := tc[i]
|
||||||
|
if current.Type > previous.Type {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current.Name > previous.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current.SemanticVersion.GreaterThan(previous.SemanticVersion) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current.Type == previous.Type && current.Name == previous.Name && current.SemanticVersion.Equal(previous.SemanticVersion) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatalf("versioned plugins at index %d and %d were not properly sorted: %+v, %+v", i-1, i, previous, current)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExternalPlugin_getBackendTypeVersion(t *testing.T) {
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
pluginType consts.PluginType
|
||||||
|
setRunningVersion string
|
||||||
|
}{
|
||||||
|
"external credential plugin": {
|
||||||
|
pluginType: consts.PluginTypeCredential,
|
||||||
|
setRunningVersion: "v1.2.3",
|
||||||
|
},
|
||||||
|
"external secrets plugin": {
|
||||||
|
pluginType: consts.PluginTypeSecrets,
|
||||||
|
setRunningVersion: "v1.2.3",
|
||||||
|
},
|
||||||
|
"external database plugin": {
|
||||||
|
pluginType: consts.PluginTypeDatabase,
|
||||||
|
setRunningVersion: "v1.2.3",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
pluginCatalog := testPluginCatalog(t)
|
||||||
|
plugin := pluginhelpers.CompilePlugin(t, tc.pluginType, tc.setRunningVersion, pluginCatalog.directory)
|
||||||
|
|
||||||
|
shaBytes, _ := hex.DecodeString(plugin.Sha256)
|
||||||
|
commandFull := filepath.Join(pluginCatalog.directory, plugin.FileName)
|
||||||
|
entry := &pluginutil.PluginRunner{
|
||||||
|
Name: plugin.Name,
|
||||||
|
Command: commandFull,
|
||||||
|
Args: nil,
|
||||||
|
Sha256: shaBytes,
|
||||||
|
Builtin: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
var version logical.PluginVersion
|
||||||
|
var err error
|
||||||
|
if tc.pluginType == consts.PluginTypeDatabase {
|
||||||
|
version, err = pluginCatalog.getDatabaseRunningVersion(context.Background(), entry)
|
||||||
|
} else {
|
||||||
|
version, err = pluginCatalog.getBackendRunningVersion(context.Background(), entry)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if version.Version != tc.setRunningVersion {
|
||||||
|
t.Errorf("Expected to get version %v but got %v", tc.setRunningVersion, version.Version)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExternalPluginInContainer_GetBackendTypeVersion(t *testing.T) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
t.Skip("Running plugins in containers is only supported on linux")
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginCatalog := testPluginCatalog(t)
|
||||||
|
|
||||||
|
var plugins []pluginhelpers.TestPlugin
|
||||||
|
for _, pluginType := range []consts.PluginType{
|
||||||
|
consts.PluginTypeCredential,
|
||||||
|
consts.PluginTypeSecrets,
|
||||||
|
consts.PluginTypeDatabase,
|
||||||
|
} {
|
||||||
|
plugin := pluginhelpers.CompilePlugin(t, pluginType, "v1.2.3", pluginCatalog.directory)
|
||||||
|
plugin.Image, plugin.ImageSha256 = pluginhelpers.BuildPluginContainerImage(t, plugin, pluginCatalog.directory)
|
||||||
|
plugins = append(plugins, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, plugin := range plugins {
|
||||||
|
t.Run(plugin.Typ.String(), func(t *testing.T) {
|
||||||
|
for _, ociRuntime := range []string{"runc", "runsc"} {
|
||||||
|
t.Run(ociRuntime, func(t *testing.T) {
|
||||||
|
if _, err := exec.LookPath(ociRuntime); err != nil {
|
||||||
|
t.Skipf("Skipping test as %s not found on path", ociRuntime)
|
||||||
|
}
|
||||||
|
|
||||||
|
shaBytes, _ := hex.DecodeString(plugin.ImageSha256)
|
||||||
|
entry := &pluginutil.PluginRunner{
|
||||||
|
Name: plugin.Name,
|
||||||
|
OCIImage: plugin.Image,
|
||||||
|
Args: nil,
|
||||||
|
Sha256: shaBytes,
|
||||||
|
Builtin: false,
|
||||||
|
Runtime: ociRuntime,
|
||||||
|
RuntimeConfig: &pluginruntimeutil.PluginRuntimeConfig{
|
||||||
|
OCIRuntime: ociRuntime,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var version logical.PluginVersion
|
||||||
|
var err error
|
||||||
|
if plugin.Typ == consts.PluginTypeDatabase {
|
||||||
|
version, err = pluginCatalog.getDatabaseRunningVersion(context.Background(), entry)
|
||||||
|
} else {
|
||||||
|
version, err = pluginCatalog.getBackendRunningVersion(context.Background(), entry)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if version.Version != plugin.Version {
|
||||||
|
t.Errorf("Expected to get version %v but got %v", plugin.Version, version.Version)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testRunTestPlugin runs the testFunc which has already been registered to the
|
||||||
|
// plugin catalog and returns a pluginClient. This can be called after calling
|
||||||
|
// TestAddTestPlugin.
|
||||||
|
func testRunTestPlugin(t *testing.T, pluginCatalog *PluginCatalog, pluginType consts.PluginType, pluginName string) *pluginClient {
|
||||||
|
t.Helper()
|
||||||
|
config := testPluginClientConfig(pluginCatalog, pluginType, pluginName)
|
||||||
|
client, err := pluginCatalog.NewPluginClient(context.Background(), config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPluginClientConfig(pluginCatalog *PluginCatalog, pluginType consts.PluginType, pluginName string) pluginutil.PluginClientConfig {
|
||||||
|
config := pluginutil.PluginClientConfig{
|
||||||
|
Name: pluginName,
|
||||||
|
PluginType: pluginType,
|
||||||
|
Logger: log.NewNullLogger(),
|
||||||
|
AutoMTLS: true,
|
||||||
|
IsMetadataMode: false,
|
||||||
|
Wrapper: pluginCatalogStaticSystemView{
|
||||||
|
pluginCatalog: pluginCatalog,
|
||||||
|
StaticSystemView: logical.StaticSystemView{
|
||||||
|
VersionString: "testVersion",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pluginType {
|
||||||
|
case consts.PluginTypeCredential, consts.PluginTypeSecrets:
|
||||||
|
config.PluginSets = backendplugin.PluginSet
|
||||||
|
config.HandshakeConfig = backendplugin.HandshakeConfig
|
||||||
|
case consts.PluginTypeDatabase:
|
||||||
|
config.PluginSets = v5.PluginSets
|
||||||
|
config.HandshakeConfig = v5.HandshakeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginCatalogStaticSystemView struct {
|
||||||
|
logical.StaticSystemView
|
||||||
|
pluginCatalog *PluginCatalog
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pluginCatalogStaticSystemView) NewPluginClient(ctx context.Context, config pluginutil.PluginClientConfig) (pluginutil.PluginClient, error) {
|
||||||
|
return p.pluginCatalog.NewPluginClient(ctx, config)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package vault
|
package plugincatalog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
pluginRuntimeCatalogPath = "core/plugin-runtime-catalog/"
|
|
||||||
ErrPluginRuntimeNotFound = errors.New("plugin runtime not found")
|
ErrPluginRuntimeNotFound = errors.New("plugin runtime not found")
|
||||||
ErrPluginRuntimeBadType = errors.New("unable to determine plugin runtime type")
|
ErrPluginRuntimeBadType = errors.New("unable to determine plugin runtime type")
|
||||||
ErrPluginRuntimeBadContainerConfig = errors.New("bad container config")
|
ErrPluginRuntimeBadContainerConfig = errors.New("bad container config")
|
||||||
@@ -29,23 +28,23 @@ var (
|
|||||||
// PluginRuntimeCatalog keeps a record of plugin runtimes. Plugin runtimes need
|
// PluginRuntimeCatalog keeps a record of plugin runtimes. Plugin runtimes need
|
||||||
// to be registered to the catalog before they can be used in backends when registering plugins with runtimes
|
// to be registered to the catalog before they can be used in backends when registering plugins with runtimes
|
||||||
type PluginRuntimeCatalog struct {
|
type PluginRuntimeCatalog struct {
|
||||||
catalogView *BarrierView
|
catalogView logical.Storage
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) setupPluginRuntimeCatalog(ctx context.Context) error {
|
func SetupPluginRuntimeCatalog(ctx context.Context, logger log.Logger, catalogView logical.Storage) (*PluginRuntimeCatalog, error) {
|
||||||
c.pluginRuntimeCatalog = &PluginRuntimeCatalog{
|
pluginRuntimeCatalog := &PluginRuntimeCatalog{
|
||||||
catalogView: NewBarrierView(c.barrier, pluginRuntimeCatalogPath),
|
catalogView: catalogView,
|
||||||
logger: c.logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.logger.IsInfo() {
|
if logger.IsInfo() {
|
||||||
c.logger.Info("successfully setup plugin runtime catalog")
|
logger.Info("successfully setup plugin runtime catalog")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return pluginRuntimeCatalog, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves a plugin runtime with the specified name from the catalog
|
// Get retrieves a plugin runtime with the specified name from the catalog
|
||||||
@@ -1,18 +1,34 @@
|
|||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package vault
|
package plugincatalog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginruntimeutil"
|
"github.com/hashicorp/vault/sdk/helper/pluginruntimeutil"
|
||||||
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/hashicorp/vault/sdk/physical/inmem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func testPluginRuntimeCatalog(t *testing.T) *PluginRuntimeCatalog {
|
||||||
|
logger := hclog.Default()
|
||||||
|
storage, err := inmem.NewInmem(nil, logger)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pluginRuntimeCatalog, err := SetupPluginRuntimeCatalog(context.Background(), logger, logical.NewLogicalStorage(storage))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return pluginRuntimeCatalog
|
||||||
|
}
|
||||||
|
|
||||||
func TestPluginRuntimeCatalog_CRUD(t *testing.T) {
|
func TestPluginRuntimeCatalog_CRUD(t *testing.T) {
|
||||||
core, _, _ := TestCoreUnsealed(t)
|
pluginRuntimeCatalog := testPluginRuntimeCatalog(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
expected := &pluginruntimeutil.PluginRuntimeConfig{
|
expected := &pluginruntimeutil.PluginRuntimeConfig{
|
||||||
@@ -24,13 +40,13 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set new plugin runtime
|
// Set new plugin runtime
|
||||||
err := core.pluginRuntimeCatalog.Set(ctx, expected)
|
err := pluginRuntimeCatalog.Set(ctx, expected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get plugin runtime
|
// Get plugin runtime
|
||||||
runner, err := core.pluginRuntimeCatalog.Get(ctx, expected.Name, expected.Type)
|
runner, err := pluginRuntimeCatalog.Get(ctx, expected.Name, expected.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -42,13 +58,13 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) {
|
|||||||
expected.CgroupParent = "memorylimit-cgroup"
|
expected.CgroupParent = "memorylimit-cgroup"
|
||||||
expected.CPU = 2
|
expected.CPU = 2
|
||||||
expected.Memory = 5000
|
expected.Memory = 5000
|
||||||
err = core.pluginRuntimeCatalog.Set(ctx, expected)
|
err = pluginRuntimeCatalog.Set(ctx, expected)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get plugin runtime again
|
// Get plugin runtime again
|
||||||
runner, err = core.pluginRuntimeCatalog.Get(ctx, expected.Name, expected.Type)
|
runner, err = pluginRuntimeCatalog.Get(ctx, expected.Name, expected.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -57,7 +73,7 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) {
|
|||||||
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", runner, expected)
|
t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", runner, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
configs, err := core.pluginRuntimeCatalog.List(ctx, expected.Type)
|
configs, err := pluginRuntimeCatalog.List(ctx, expected.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
@@ -66,13 +82,13 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete plugin runtime
|
// Delete plugin runtime
|
||||||
err = core.pluginRuntimeCatalog.Delete(ctx, expected.Name, expected.Type)
|
err = pluginRuntimeCatalog.Delete(ctx, expected.Name, expected.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert the plugin runtime catalog is empty
|
// Assert the plugin runtime catalog is empty
|
||||||
configs, err = core.pluginRuntimeCatalog.List(ctx, expected.Type)
|
configs, err = pluginRuntimeCatalog.List(ctx, expected.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
94
vault/plugincatalog/testing.go
Normal file
94
vault/plugincatalog/testing.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package plugincatalog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
|
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||||
|
"github.com/mitchellh/go-testing-interface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestAddTestPlugin registers the testFunc as part of the plugin command to the
|
||||||
|
// plugin catalog. The plugin catalog must be configured with a pluginDirectory.
|
||||||
|
// NB: The test func you pass in MUST be in the same package as the parent test,
|
||||||
|
// or the test func won't be compiled into the test binary being run and the output
|
||||||
|
// will be something like:
|
||||||
|
// stderr (ignored by go-plugin): "testing: warning: no tests to run"
|
||||||
|
// stdout: "PASS"
|
||||||
|
func TestAddTestPlugin(t testing.T, pluginCatalog *PluginCatalog, name string, pluginType consts.PluginType, version string, testFunc string, env []string) {
|
||||||
|
t.Helper()
|
||||||
|
if pluginCatalog.directory == "" {
|
||||||
|
t.Fatal("plugin catalog must have a plugin directory set to add plugins")
|
||||||
|
}
|
||||||
|
file, err := os.Open(os.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
fileName := filepath.Base(os.Args[0])
|
||||||
|
|
||||||
|
fi, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over the file to the temp dir
|
||||||
|
dst := filepath.Join(pluginCatalog.directory, fileName)
|
||||||
|
|
||||||
|
// delete the file first to avoid notary failures in macOS
|
||||||
|
_ = os.Remove(dst) // ignore error
|
||||||
|
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
if _, err = io.Copy(out, file); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = out.Sync()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Ensure that the file is closed and written. This seems to be
|
||||||
|
// necessary on Linux systems.
|
||||||
|
out.Close()
|
||||||
|
|
||||||
|
// Copied the file, now seek to the start again to calculate its sha256 hash.
|
||||||
|
_, err = file.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
_, err = io.Copy(hash, file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sum := hash.Sum(nil)
|
||||||
|
|
||||||
|
// The flag is a regex, so use ^$ to make sure we only run a single test
|
||||||
|
// with an exact match.
|
||||||
|
args := []string{fmt.Sprintf("--test.run=^%s$", testFunc)}
|
||||||
|
err = pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
||||||
|
Name: name,
|
||||||
|
Type: pluginType,
|
||||||
|
Version: version,
|
||||||
|
Command: fileName,
|
||||||
|
Args: args,
|
||||||
|
Env: env,
|
||||||
|
Sha256: sum,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
143
vault/testing.go
143
vault/testing.go
@@ -9,7 +9,6 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
@@ -17,7 +16,6 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
@@ -52,17 +50,15 @@ import (
|
|||||||
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
"github.com/hashicorp/vault/helper/testhelpers/corehelpers"
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
"github.com/hashicorp/vault/helper/testhelpers/pluginhelpers"
|
||||||
"github.com/hashicorp/vault/internalshared/configutil"
|
"github.com/hashicorp/vault/internalshared/configutil"
|
||||||
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
|
||||||
"github.com/hashicorp/vault/sdk/framework"
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/logging"
|
"github.com/hashicorp/vault/sdk/helper/logging"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
|
||||||
"github.com/hashicorp/vault/sdk/helper/testcluster"
|
"github.com/hashicorp/vault/sdk/helper/testcluster"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/hashicorp/vault/sdk/physical"
|
"github.com/hashicorp/vault/sdk/physical"
|
||||||
physInmem "github.com/hashicorp/vault/sdk/physical/inmem"
|
physInmem "github.com/hashicorp/vault/sdk/physical/inmem"
|
||||||
backendplugin "github.com/hashicorp/vault/sdk/plugin"
|
|
||||||
"github.com/hashicorp/vault/vault/cluster"
|
"github.com/hashicorp/vault/vault/cluster"
|
||||||
|
"github.com/hashicorp/vault/vault/plugincatalog"
|
||||||
"github.com/hashicorp/vault/vault/seal"
|
"github.com/hashicorp/vault/vault/seal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -543,136 +539,9 @@ func TestDynamicSystemView(c *Core, ns *namespace.Namespace) *dynamicSystemView
|
|||||||
return &dynamicSystemView{c, me, c.perfStandby}
|
return &dynamicSystemView{c, me, c.perfStandby}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAddTestPlugin registers the testFunc as part of the plugin command to the
|
func TestAddTestPlugin(t testing.T, core *Core, name string, pluginType consts.PluginType, version string, testFunc string, env []string) {
|
||||||
// plugin catalog. If provided, uses tmpDir as the plugin directory.
|
|
||||||
// NB: The test func you pass in MUST be in the same package as the parent test,
|
|
||||||
// or the test func won't be compiled into the test binary being run and the output
|
|
||||||
// will be something like:
|
|
||||||
// stderr (ignored by go-plugin): "testing: warning: no tests to run"
|
|
||||||
// stdout: "PASS"
|
|
||||||
func TestAddTestPlugin(t testing.T, c *Core, name string, pluginType consts.PluginType, version string, testFunc string, env []string, tempDir string) {
|
|
||||||
file, err := os.Open(os.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
dirPath := filepath.Dir(os.Args[0])
|
|
||||||
fileName := filepath.Base(os.Args[0])
|
|
||||||
|
|
||||||
if tempDir != "" {
|
|
||||||
fi, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy over the file to the temp dir
|
|
||||||
dst := filepath.Join(tempDir, fileName)
|
|
||||||
|
|
||||||
// delete the file first to avoid notary failures in macOS
|
|
||||||
_ = os.Remove(dst) // ignore error
|
|
||||||
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
if _, err = io.Copy(out, file); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = out.Sync()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Ensure that the file is closed and written. This seems to be
|
|
||||||
// necessary on Linux systems.
|
|
||||||
out.Close()
|
|
||||||
|
|
||||||
dirPath = tempDir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine plugin directory full path, evaluating potential symlink path
|
|
||||||
fullPath, err := filepath.EvalSymlinks(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := os.Open(filepath.Join(fullPath, fileName))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer reader.Close()
|
|
||||||
|
|
||||||
// Find out the sha256
|
|
||||||
hash := sha256.New()
|
|
||||||
|
|
||||||
_, err = io.Copy(hash, reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sum := hash.Sum(nil)
|
|
||||||
|
|
||||||
// Set core's plugin directory and plugin catalog directory
|
|
||||||
c.pluginDirectory = fullPath
|
|
||||||
c.pluginCatalog.directory = fullPath
|
|
||||||
|
|
||||||
args := []string{fmt.Sprintf("--test.run=%s", testFunc)}
|
|
||||||
err = c.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{
|
|
||||||
Name: name,
|
|
||||||
Type: pluginType,
|
|
||||||
Version: version,
|
|
||||||
Command: fileName,
|
|
||||||
Args: args,
|
|
||||||
Env: env,
|
|
||||||
Sha256: sum,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRunTestPlugin runs the testFunc which has already been registered to the
|
|
||||||
// plugin catalog and returns a pluginClient. This can be called after calling
|
|
||||||
// TestAddTestPlugin.
|
|
||||||
func TestRunTestPlugin(t testing.T, c *Core, pluginType consts.PluginType, pluginName string) *pluginClient {
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
config := TestPluginClientConfig(c, pluginType, pluginName)
|
plugincatalog.TestAddTestPlugin(t, core.pluginCatalog, name, pluginType, version, testFunc, env)
|
||||||
client, err := c.pluginCatalog.NewPluginClient(context.Background(), config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPluginClientConfig(c *Core, pluginType consts.PluginType, pluginName string) pluginutil.PluginClientConfig {
|
|
||||||
dsv := TestDynamicSystemView(c, nil)
|
|
||||||
switch pluginType {
|
|
||||||
case consts.PluginTypeCredential, consts.PluginTypeSecrets:
|
|
||||||
return pluginutil.PluginClientConfig{
|
|
||||||
Name: pluginName,
|
|
||||||
PluginType: pluginType,
|
|
||||||
PluginSets: backendplugin.PluginSet,
|
|
||||||
HandshakeConfig: backendplugin.HandshakeConfig,
|
|
||||||
Logger: log.NewNullLogger(),
|
|
||||||
AutoMTLS: true,
|
|
||||||
IsMetadataMode: false,
|
|
||||||
Wrapper: dsv,
|
|
||||||
}
|
|
||||||
case consts.PluginTypeDatabase:
|
|
||||||
return pluginutil.PluginClientConfig{
|
|
||||||
Name: pluginName,
|
|
||||||
PluginType: pluginType,
|
|
||||||
PluginSets: v5.PluginSets,
|
|
||||||
HandshakeConfig: v5.HandshakeConfig,
|
|
||||||
Logger: log.NewNullLogger(),
|
|
||||||
AutoMTLS: true,
|
|
||||||
IsMetadataMode: false,
|
|
||||||
Wrapper: dsv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pluginutil.PluginClientConfig{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -1707,13 +1576,9 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te
|
|||||||
t.Skip("Running plugins in containers is only supported on linux")
|
t.Skip("Running plugins in containers is only supported on linux")
|
||||||
}
|
}
|
||||||
|
|
||||||
var pluginDir string
|
|
||||||
var cleanup func(t testing.T)
|
|
||||||
|
|
||||||
if coreConfig.PluginDirectory == "" {
|
if coreConfig.PluginDirectory == "" {
|
||||||
pluginDir, cleanup = corehelpers.MakeTestPluginDir(t)
|
pluginDir := corehelpers.MakeTestPluginDir(t)
|
||||||
coreConfig.PluginDirectory = pluginDir
|
coreConfig.PluginDirectory = pluginDir
|
||||||
t.Cleanup(func() { cleanup(t) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, version := range pluginType.Versions {
|
for _, version := range pluginType.Versions {
|
||||||
|
|||||||
Reference in New Issue
Block a user