Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugins: Auto version selection for auth/secrets + tune version #17167

Merged
merged 5 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions vault/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1889,10 +1889,11 @@ func testCore_Standby_Common(t *testing.T, inm physical.Backend, inmha physical.
// Create the first core and initialize it
redirectOriginal := "http://127.0.0.1:8200"
core, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
RedirectAddr: redirectOriginal,
DisableMlock: true,
Physical: inm,
HAPhysical: inmha,
RedirectAddr: redirectOriginal,
DisableMlock: true,
BuiltinRegistry: NewMockBuiltinRegistry(),
})
if err != nil {
t.Fatalf("err: %v", err)
Expand Down
195 changes: 78 additions & 117 deletions vault/external_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"os"
"os/exec"
Expand Down Expand Up @@ -155,34 +154,9 @@ func TestCore_EnableExternalPlugin(t *testing.T) {
} {
t.Run(name, func(t *testing.T) {
c, pluginName, pluginSHA256 := testCoreWithPlugin(t, tc.pluginType, "")
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSHA256,
"version": "v1.0.0",
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
registerPlugin(t, c.systemBackend, pluginName, tc.pluginType.String(), "1.0.0", pluginSHA256)

me := &MountEntry{
Table: mountTable(tc.pluginType),
Path: "foo",
Type: pluginName,
Version: "v1.0.0",
}
enable := enableFunc(c, tc.pluginType)
err = enable(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
mountPlugin(t, c.systemBackend, pluginName, tc.pluginType, "v1.0.0")

match := c.router.MatchingMount(namespace.RootContext(nil), tc.routerPath)
if match != tc.expectedMatch {
Expand All @@ -197,79 +171,75 @@ func TestCore_EnableExternalPlugin_MultipleVersions(t *testing.T) {
pluginType consts.PluginType
registerVersions []string
mountVersion string
expectedVersion string
routerPath string
expectedMatch string
}{
"enable external credential plugin, multiple versions available": {
pluginType: consts.PluginTypeCredential,
registerVersions: []string{"v1.0.0", "v1.0.1"},
mountVersion: "v1.0.0",
expectedVersion: "v1.0.0",
routerPath: "auth/foo/bar",
expectedMatch: "auth/foo/",
},
"enable external secrets plugin, multiple versions available": {
pluginType: consts.PluginTypeSecrets,
registerVersions: []string{"v1.0.0", "v1.0.1"},
mountVersion: "v1.0.0",
expectedVersion: "v1.0.0",
routerPath: "foo/bar",
expectedMatch: "foo/",
},
"enable external credential plugin, multiple versions available, select other version": {
pluginType: consts.PluginTypeCredential,
registerVersions: []string{"v1.0.0", "v1.0.1"},
mountVersion: "v1.0.1",
expectedVersion: "v1.0.1",
routerPath: "auth/foo/bar",
expectedMatch: "auth/foo/",
},
"enable external secrets plugin, multiple versions available, select other version": {
pluginType: consts.PluginTypeSecrets,
registerVersions: []string{"v1.0.0", "v1.0.1"},
mountVersion: "v1.0.1",
expectedVersion: "v1.0.1",
routerPath: "foo/bar",
expectedMatch: "foo/",
},
tvoran marked this conversation as resolved.
Show resolved Hide resolved
"enable external credential plugin, selects latest when version not specified": {
pluginType: consts.PluginTypeCredential,
registerVersions: []string{"v1.0.0", "v1.0.1"},
mountVersion: "",
expectedVersion: "v1.0.1",
routerPath: "auth/foo/bar",
expectedMatch: "auth/foo/",
},
"enable external secrets plugin, selects latest when version not specified": {
pluginType: consts.PluginTypeSecrets,
registerVersions: []string{"v1.0.0", "v1.0.1"},
mountVersion: "",
expectedVersion: "v1.0.1",
routerPath: "foo/bar",
expectedMatch: "foo/",
},
} {
t.Run(name, func(t *testing.T) {
c, pluginName, pluginSHA256 := testCoreWithPlugin(t, tc.pluginType, "")
for _, version := range tc.registerVersions {
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSHA256,
"version": version,
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
registerPlugin(t, c.systemBackend, pluginName, tc.pluginType.String(), version, pluginSHA256)
}

me := &MountEntry{
Table: mountTable(tc.pluginType),
Path: "foo",
Type: pluginName,
Version: tc.mountVersion,
}
enable := enableFunc(c, tc.pluginType)
err := enable(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
mountPlugin(t, c.systemBackend, pluginName, tc.pluginType, tc.mountVersion)

match := c.router.MatchingMount(namespace.RootContext(nil), tc.routerPath)
if match != tc.expectedMatch {
t.Fatalf("missing mount, match: %q", match)
}

raw, _ := c.router.root.Get(match)
if raw.(*routeEntry).mountEntry.Version != tc.mountVersion {
t.Errorf("Expected mount to be version %s but got %s", tc.mountVersion, raw.(*routeEntry).mountEntry.Version)
if raw.(*routeEntry).mountEntry.Version != tc.expectedVersion {
t.Errorf("Expected mount to be version %s but got %s", tc.expectedVersion, raw.(*routeEntry).mountEntry.Version)
}

// we don't override the running version of non-builtins, and they don't have the version set explicitly (yet)
Expand Down Expand Up @@ -303,32 +273,14 @@ func TestCore_EnableExternalPlugin_NoVersionsOkay(t *testing.T) {
} {
t.Run(name, func(t *testing.T) {
c, pluginName, pluginSHA256 := testCoreWithPlugin(t, tc.pluginType, "")
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSHA256,
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
// When an unversioned plugin is registered, mounting a plugin with no
// version specified should mount the unversioned plugin even if there
// are versioned plugins available.
for _, version := range []string{"", "v1.0.0"} {
registerPlugin(t, c.systemBackend, pluginName, tc.pluginType.String(), version, pluginSHA256)
}

me := &MountEntry{
Table: mountTable(tc.pluginType),
Path: "foo",
Type: pluginName,
}
enable := enableFunc(c, tc.pluginType)
err = enable(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
mountPlugin(t, c.systemBackend, pluginName, tc.pluginType, "")

match := c.router.MatchingMount(namespace.RootContext(nil), tc.routerPath)
if match != tc.expectedMatch {
Expand Down Expand Up @@ -362,32 +314,16 @@ func TestCore_EnableExternalCredentialPlugin_NoVersionOnRegister(t *testing.T) {
} {
t.Run(name, func(t *testing.T) {
c, pluginName, pluginSHA256 := testCoreWithPlugin(t, tc.pluginType, "")
d := &framework.FieldData{
Raw: map[string]interface{}{
"name": pluginName,
"sha256": pluginSHA256,
"command": pluginName,
},
Schema: c.systemBackend.pluginsCatalogCRUDPath().Fields,
}
resp, err := c.systemBackend.handlePluginCatalogUpdate(context.Background(), nil, d)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
registerPlugin(t, c.systemBackend, pluginName, tc.pluginType.String(), "", pluginSHA256)

me := &MountEntry{
Table: mountTable(tc.pluginType),
Path: "foo",
Type: pluginName,
Version: "v1.0.0",
req := logical.TestRequest(t, logical.UpdateOperation, mountTable(tc.pluginType))
req.Data = map[string]interface{}{
"type": pluginName,
"plugin_version": "v1.0.0",
}
enable := enableFunc(c, tc.pluginType)
err = enable(namespace.RootContext(nil), me)
if err == nil || !errors.Is(err, ErrPluginNotFound) {
t.Fatalf("Expected to get plugin not found but got: %v", err)
resp, _ := c.systemBackend.HandleRequest(namespace.RootContext(nil), req)
if resp == nil || !resp.IsError() || !strings.Contains(resp.Error().Error(), ErrPluginNotFound.Error()) {
t.Fatalf("Expected to get plugin not found but got: %v", resp.Error())
}
})
}
Expand Down Expand Up @@ -486,24 +422,49 @@ func TestExternalPlugin_getBackendTypeVersion(t *testing.T) {
}
}

func mountTable(pluginType consts.PluginType) string {
switch pluginType {
case consts.PluginTypeCredential:
return credentialTableType
case consts.PluginTypeSecrets:
return mountTableType
default:
panic("test does not support plugin type yet")
func registerPlugin(t *testing.T, sys *SystemBackend, pluginName, pluginType, version, sha string) {
t.Helper()
req := logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("plugins/catalog/%s/%s", pluginType, pluginName))
req.Data = map[string]interface{}{
"name": pluginName,
"command": pluginName,
"sha256": sha,
"version": version,
}
resp, err := sys.HandleRequest(namespace.RootContext(nil), req)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
}

func enableFunc(c *Core, pluginType consts.PluginType) func(context.Context, *MountEntry) error {
func mountPlugin(t *testing.T, sys *SystemBackend, pluginName string, pluginType consts.PluginType, version string) {
t.Helper()
req := logical.TestRequest(t, logical.UpdateOperation, mountTable(pluginType))
req.Data = map[string]interface{}{
"type": pluginName,
}
if version != "" {
req.Data["plugin_version"] = version
}
resp, err := sys.HandleRequest(namespace.RootContext(nil), req)
if err != nil {
t.Fatal(err)
}
if resp.Error() != nil {
t.Fatalf("%#v", resp)
}
}

func mountTable(pluginType consts.PluginType) string {
switch pluginType {
case consts.PluginTypeCredential:
return c.enableCredential
return "auth/foo"
case consts.PluginTypeSecrets:
return c.mount
return "mounts/foo"
default:
panic(pluginType.String())
panic("test does not support plugin type yet")
}
}