Add plugin version to GRPC interface (#17088)
Add plugin version to GRPC interface Added a version interface in the sdk/logical so that it can be shared between all plugin types, and then wired it up to RunningVersion in the mounts, auth list, and database systems. I've tested that this works with auth, database, and secrets plugin types, with the following logic to populate RunningVersion: If a plugin has a PluginVersion() method implemented, then that is used If not, and the plugin is built into the Vault binary, then the go.mod version is used Otherwise, the it will be the empty string. My apologies for the length of this PR. * Placeholder backend should be external We use a placeholder backend (previously a framework.Backend) before a GRPC plugin is lazy-loaded. This makes us later think the plugin is a builtin plugin. So we added a `placeholderBackend` type that overrides the `IsExternal()` method so that later we know that the plugin is external, and don't give it a default builtin version.
This commit is contained in:
parent
aa503ef7ff
commit
b136a7ecd8
5
Makefile
5
Makefile
|
@ -12,6 +12,7 @@ EXTERNAL_TOOLS_CI=\
|
|||
EXTERNAL_TOOLS=\
|
||||
github.com/client9/misspell/cmd/misspell
|
||||
GOFMT_FILES?=$$(find . -name '*.go' | grep -v pb.go | grep -v vendor)
|
||||
SED?=$(shell command -v gsed || command -v sed)
|
||||
|
||||
|
||||
GO_VERSION_MIN=1.19.1
|
||||
|
@ -190,8 +191,8 @@ proto: bootstrap
|
|||
# No additional sed expressions should be added to this list. Going forward
|
||||
# we should just use the variable names choosen by protobuf. These are left
|
||||
# here for backwards compatability, namely for SDK compilation.
|
||||
sed -i -e 's/Id/ID/' vault/request_forwarding_service.pb.go
|
||||
sed -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/IDentity/Identity/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/Totp/TOTP/' -e 's/Mfa/MFA/' -e 's/Pingid/PingID/' -e 's/namespaceId/namespaceID/' -e 's/Ttl/TTL/' -e 's/BoundCidrs/BoundCIDRs/' helper/identity/types.pb.go helper/identity/mfa/types.pb.go helper/storagepacker/types.pb.go sdk/plugin/pb/backend.pb.go sdk/logical/identity.pb.go vault/activity/activity_log.pb.go
|
||||
$(SED) -i -e 's/Id/ID/' vault/request_forwarding_service.pb.go
|
||||
$(SED) -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/IDentity/Identity/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/Totp/TOTP/' -e 's/Mfa/MFA/' -e 's/Pingid/PingID/' -e 's/namespaceId/namespaceID/' -e 's/Ttl/TTL/' -e 's/BoundCidrs/BoundCIDRs/' helper/identity/types.pb.go helper/identity/mfa/types.pb.go helper/storagepacker/types.pb.go sdk/plugin/pb/backend.pb.go sdk/logical/identity.pb.go vault/activity/activity_log.pb.go
|
||||
|
||||
# This will inject the sentinel struct tags as decorated in the proto files.
|
||||
protoc-go-inject-tag -input=./helper/identity/types.pb.go
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
|
||||
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
@ -18,6 +19,8 @@ type databaseVersionWrapper struct {
|
|||
v5 v5.Database
|
||||
}
|
||||
|
||||
var _ logical.PluginVersioner = databaseVersionWrapper{}
|
||||
|
||||
// newDatabaseWrapper figures out which version of the database the pluginName is referring to and returns a wrapper object
|
||||
// that can be used to make operations on the underlying database plugin.
|
||||
func newDatabaseWrapper(ctx context.Context, pluginName string, pluginVersion string, sys pluginutil.LookRunnerUtil, logger log.Logger) (dbw databaseVersionWrapper, err error) {
|
||||
|
@ -227,6 +230,21 @@ func (d databaseVersionWrapper) Close() error {
|
|||
return d.v4.Close()
|
||||
}
|
||||
|
||||
func (d databaseVersionWrapper) PluginVersion() logical.PluginVersion {
|
||||
// v5 Database
|
||||
if d.isV5() {
|
||||
if versioner, ok := d.v5.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
}
|
||||
|
||||
// v4 Database
|
||||
if versioner, ok := d.v4.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
func (d databaseVersionWrapper) isV5() bool {
|
||||
return d.v5 != nil
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
log "github.com/hashicorp/go-hclog"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
v5 "github.com/hashicorp/vault/builtin/plugin/v5"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
|
@ -79,15 +79,28 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (*PluginBackend,
|
|||
// Get SpecialPaths and BackendType
|
||||
paths := raw.SpecialPaths()
|
||||
btype := raw.Type()
|
||||
runningVersion := ""
|
||||
if versioner, ok := raw.(logical.PluginVersioner); ok {
|
||||
runningVersion = versioner.PluginVersion().Version
|
||||
}
|
||||
|
||||
external := false
|
||||
if externaler, ok := raw.(logical.Externaler); ok {
|
||||
external = externaler.IsExternal()
|
||||
}
|
||||
|
||||
// Cleanup meta plugin backend
|
||||
raw.Cleanup(ctx)
|
||||
|
||||
// Initialize b.Backend with dummy backend since plugin
|
||||
// Initialize b.Backend with placeholder backend since plugin
|
||||
// backends will need to be lazy loaded.
|
||||
b.Backend = &framework.Backend{
|
||||
b.Backend = &placeholderBackend{
|
||||
Backend: framework.Backend{
|
||||
PathsSpecial: paths,
|
||||
BackendType: btype,
|
||||
RunningVersion: runningVersion,
|
||||
},
|
||||
external: external,
|
||||
}
|
||||
|
||||
b.config = conf
|
||||
|
@ -95,6 +108,23 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (*PluginBackend,
|
|||
return &b, nil
|
||||
}
|
||||
|
||||
// placeholderBackend is used a placeholder before a backend is lazy-loaded.
|
||||
// It is mostly used to mark that the backend is an external backend.
|
||||
type placeholderBackend struct {
|
||||
framework.Backend
|
||||
|
||||
external bool
|
||||
}
|
||||
|
||||
func (p *placeholderBackend) IsExternal() bool {
|
||||
return p.external
|
||||
}
|
||||
|
||||
var (
|
||||
_ logical.Externaler = (*placeholderBackend)(nil)
|
||||
_ logical.PluginVersioner = (*placeholderBackend)(nil)
|
||||
)
|
||||
|
||||
// PluginBackend is a thin wrapper around plugin.BackendPluginClient
|
||||
type PluginBackend struct {
|
||||
Backend logical.Backend
|
||||
|
@ -286,3 +316,22 @@ func (b *PluginBackend) Type() logical.BackendType {
|
|||
defer b.RUnlock()
|
||||
return b.Backend.Type()
|
||||
}
|
||||
|
||||
func (b *PluginBackend) PluginVersion() logical.PluginVersion {
|
||||
if versioner, ok := b.Backend.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
func (b *PluginBackend) IsExternal() bool {
|
||||
if externaler, ok := b.Backend.(logical.Externaler); ok {
|
||||
return externaler.IsExternal()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
_ logical.PluginVersioner = (*PluginBackend)(nil)
|
||||
_ logical.Externaler = (*PluginBackend)(nil)
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/rpc"
|
||||
"sync"
|
||||
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin"
|
||||
|
@ -35,7 +35,7 @@ func Backend(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
|
|||
return &b, nil
|
||||
}
|
||||
|
||||
// backend is a thin wrapper around plugin.BackendPluginClientV5
|
||||
// backend is a thin wrapper around a builtin plugin or a plugin.BackendPluginClientV5
|
||||
type backend struct {
|
||||
logical.Backend
|
||||
mu sync.RWMutex
|
||||
|
@ -147,3 +147,11 @@ func (b *backend) InvalidateKey(ctx context.Context, key string) {
|
|||
defer b.mu.RUnlock()
|
||||
b.Backend.InvalidateKey(ctx, key)
|
||||
}
|
||||
|
||||
func (b *backend) IsExternal() bool {
|
||||
switch b.Backend.(type) {
|
||||
case *plugin.BackendPluginClientV5:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
plugins: Adding version to plugin GRPC interface
|
||||
```
|
|
@ -0,0 +1,54 @@
|
|||
package versions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/version"
|
||||
)
|
||||
|
||||
var (
|
||||
buildInfoOnce sync.Once // once is used to ensure we only parse build info once.
|
||||
buildInfo *debug.BuildInfo
|
||||
DefaultBuiltinVersion = "v" + version.GetVersion().Version + "+builtin.vault"
|
||||
)
|
||||
|
||||
func GetBuiltinVersion(pluginType consts.PluginType, pluginName string) string {
|
||||
buildInfoOnce.Do(func() {
|
||||
buildInfo, _ = debug.ReadBuildInfo()
|
||||
})
|
||||
|
||||
// Should never happen, means the binary was built without Go modules.
|
||||
// Fall back to just the Vault version.
|
||||
if buildInfo == nil {
|
||||
return DefaultBuiltinVersion
|
||||
}
|
||||
|
||||
// Vault builtin plugins are all either:
|
||||
// a) An external repo within the hashicorp org - return external repo version with +builtin
|
||||
// b) Within the Vault repo itself - return Vault version with +builtin.vault
|
||||
//
|
||||
// The repo names are predictable, but follow slightly different patterns
|
||||
// for each plugin type.
|
||||
t := pluginType.String()
|
||||
switch pluginType {
|
||||
case consts.PluginTypeDatabase:
|
||||
// Database plugin built-ins are registered as e.g. "postgresql-database-plugin"
|
||||
pluginName = strings.TrimSuffix(pluginName, "-database-plugin")
|
||||
case consts.PluginTypeSecrets:
|
||||
// Repos use "secrets", pluginType.String() is "secret".
|
||||
t = "secrets"
|
||||
}
|
||||
pluginModulePath := fmt.Sprintf("github.com/hashicorp/vault-plugin-%s-%s", t, pluginName)
|
||||
|
||||
for _, dep := range buildInfo.Deps {
|
||||
if dep.Path == pluginModulePath {
|
||||
return dep.Version + "+builtin"
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultBuiltinVersion
|
||||
}
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
|
@ -415,7 +416,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -433,7 +434,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -450,7 +451,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -468,7 +469,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -486,7 +487,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -504,7 +505,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -521,7 +522,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -539,7 +540,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
|
@ -128,7 +130,7 @@ func TestSysEnableAuth(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeCredential, "approle"),
|
||||
"version": "",
|
||||
},
|
||||
"token/": map[string]interface{}{
|
||||
|
@ -166,7 +168,7 @@ func TestSysEnableAuth(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeCredential, "approle"),
|
||||
"version": "",
|
||||
},
|
||||
"token/": map[string]interface{}{
|
||||
|
@ -531,7 +533,7 @@ func TestSysRemountAuth(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"token/": map[string]interface{}{
|
||||
|
@ -568,7 +570,7 @@ func TestSysRemountAuth(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"token/": map[string]interface{}{
|
||||
|
|
|
@ -7,9 +7,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
|
@ -44,7 +45,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -62,7 +63,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -79,7 +80,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -97,7 +98,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -115,7 +116,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -133,7 +134,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -150,7 +151,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -168,7 +169,7 @@ func TestSysMounts(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
@ -233,7 +234,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -250,7 +251,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -268,7 +269,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -285,7 +286,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -303,7 +304,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -321,7 +322,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -338,7 +339,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -356,7 +357,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -373,7 +374,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -391,7 +392,7 @@ func TestSysMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
@ -491,7 +492,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -508,7 +509,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -526,7 +527,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -543,7 +544,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -561,7 +562,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -579,7 +580,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -596,7 +597,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -614,7 +615,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -631,7 +632,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -649,7 +650,7 @@ func TestSysRemount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
@ -714,7 +715,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -732,7 +733,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -749,7 +750,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -767,7 +768,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -785,7 +786,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -803,7 +804,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -820,7 +821,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -838,7 +839,7 @@ func TestSysUnmount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
@ -989,7 +990,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -1006,7 +1007,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -1024,7 +1025,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -1041,7 +1042,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -1059,7 +1060,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -1077,7 +1078,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -1094,7 +1095,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -1112,7 +1113,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -1129,7 +1130,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -1147,7 +1148,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
@ -1238,7 +1239,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -1255,7 +1256,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -1273,7 +1274,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -1290,7 +1291,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -1308,7 +1309,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
},
|
||||
|
@ -1326,7 +1327,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"secret/": map[string]interface{}{
|
||||
|
@ -1343,7 +1344,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": map[string]interface{}{"version": "1"},
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
|
@ -1361,7 +1362,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
"version": "",
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
|
@ -1378,7 +1379,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
"version": "",
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
|
@ -1396,7 +1397,7 @@ func TestSysTuneMount(t *testing.T) {
|
|||
"options": interface{}(nil),
|
||||
"sha": "",
|
||||
"running_sha": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
"version": "",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -9,19 +9,30 @@ import (
|
|||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/hashicorp/vault/sdk/database/dbplugin/v5/proto"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Database = gRPCClient{}
|
||||
_ logical.PluginVersioner = gRPCClient{}
|
||||
|
||||
ErrPluginShutdown = errors.New("plugin shutdown")
|
||||
)
|
||||
|
||||
type gRPCClient struct {
|
||||
client proto.DatabaseClient
|
||||
versionClient logical.PluginVersionClient
|
||||
doneCtx context.Context
|
||||
}
|
||||
|
||||
func (c gRPCClient) PluginVersion() logical.PluginVersion {
|
||||
version, _ := c.versionClient.Version(context.Background(), &logical.Empty{})
|
||||
if version != nil {
|
||||
return logical.PluginVersion{Version: version.PluginVersion}
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
func (c gRPCClient) Initialize(ctx context.Context, req InitializeRequest) (InitializeResponse, error) {
|
||||
rpcReq, err := initReqToProto(req)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/database/dbplugin/v5/proto"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
|
@ -54,12 +55,14 @@ func (d GRPCDatabasePlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) err
|
|||
}
|
||||
|
||||
proto.RegisterDatabaseServer(s, &server)
|
||||
logical.RegisterPluginVersionServer(s, &server)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (GRPCDatabasePlugin) GRPCClient(doneCtx context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
client := gRPCClient{
|
||||
client: proto.NewDatabaseClient(c),
|
||||
versionClient: logical.NewPluginVersionClient(c),
|
||||
doneCtx: doneCtx,
|
||||
}
|
||||
return client, nil
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/hashicorp/vault/sdk/database/dbplugin/v5/proto"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
@ -17,6 +18,7 @@ var _ proto.DatabaseServer = &gRPCServer{}
|
|||
|
||||
type gRPCServer struct {
|
||||
proto.UnimplementedDatabaseServer
|
||||
logical.UnimplementedPluginVersionServer
|
||||
|
||||
// holds the non-multiplexed Database
|
||||
// when this is set the plugin does not support multiplexing
|
||||
|
@ -302,6 +304,18 @@ func (g *gRPCServer) Close(ctx context.Context, _ *proto.Empty) (*proto.Empty, e
|
|||
return &proto.Empty{}, nil
|
||||
}
|
||||
|
||||
// Version forwards the version request to the underlying Database implementation.
|
||||
func (g *gRPCServer) Version(ctx context.Context, _ *logical.Empty) (*logical.VersionReply, error) {
|
||||
impl, err := g.getDatabaseInternal(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if versioner, ok := impl.(logical.PluginVersioner); ok {
|
||||
return &logical.VersionReply{PluginVersion: versioner.PluginVersion().Version}, nil
|
||||
}
|
||||
return &logical.VersionReply{}, nil
|
||||
}
|
||||
|
||||
func getStatementsFromProto(protoStmts *proto.Statements) (statements Statements) {
|
||||
if protoStmts == nil {
|
||||
return statements
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
@ -581,6 +582,55 @@ func TestGRPCServer_Close(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGRPCServer_Version(t *testing.T) {
|
||||
type testCase struct {
|
||||
db Database
|
||||
expectedResp string
|
||||
expectErr bool
|
||||
expectCode codes.Code
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"backend that does not implement version": {
|
||||
db: fakeDatabase{},
|
||||
expectedResp: "",
|
||||
expectErr: false,
|
||||
expectCode: codes.OK,
|
||||
},
|
||||
"backend with version": {
|
||||
db: fakeDatabaseWithVersion{
|
||||
version: "v123",
|
||||
},
|
||||
expectedResp: "v123",
|
||||
expectErr: false,
|
||||
expectCode: codes.OK,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
idCtx, g := testGrpcServer(t, test.db)
|
||||
resp, err := g.Version(idCtx, &logical.Empty{})
|
||||
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
actualCode := status.Code(err)
|
||||
if actualCode != test.expectCode {
|
||||
t.Fatalf("Actual code: %s Expected code: %s", actualCode, test.expectCode)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(resp.PluginVersion, test.expectedResp) {
|
||||
t.Fatalf("Actual response: %#v\nExpected response: %#v", resp, test.expectedResp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// testGrpcServer is a test helper that returns a context with an ID set in its
|
||||
// metadata and a gRPCServer instance for a multiplexed plugin
|
||||
func testGrpcServer(t *testing.T, db Database) (context.Context, gRPCServer) {
|
||||
|
@ -747,3 +797,40 @@ func (f *recordingDatabase) Close() error {
|
|||
}
|
||||
return f.next.Close()
|
||||
}
|
||||
|
||||
type fakeDatabaseWithVersion struct {
|
||||
version string
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) PluginVersion() logical.PluginVersion {
|
||||
return logical.PluginVersion{Version: e.version}
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) Initialize(_ context.Context, _ InitializeRequest) (InitializeResponse, error) {
|
||||
return InitializeResponse{}, nil
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) NewUser(_ context.Context, _ NewUserRequest) (NewUserResponse, error) {
|
||||
return NewUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) UpdateUser(_ context.Context, _ UpdateUserRequest) (UpdateUserResponse, error) {
|
||||
return UpdateUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) DeleteUser(_ context.Context, _ DeleteUserRequest) (DeleteUserResponse, error) {
|
||||
return DeleteUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) Type() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (e fakeDatabaseWithVersion) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ Database = (*fakeDatabaseWithVersion)(nil)
|
||||
_ logical.PluginVersioner = (*fakeDatabaseWithVersion)(nil)
|
||||
)
|
||||
|
|
|
@ -7,9 +7,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/errwrap"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
|
@ -17,7 +18,10 @@ import (
|
|||
// Tracing Middleware
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
var _ Database = databaseTracingMiddleware{}
|
||||
var (
|
||||
_ Database = databaseTracingMiddleware{}
|
||||
_ logical.PluginVersioner = databaseTracingMiddleware{}
|
||||
)
|
||||
|
||||
// databaseTracingMiddleware wraps a implementation of Database and executes
|
||||
// trace logging on function call.
|
||||
|
@ -26,6 +30,21 @@ type databaseTracingMiddleware struct {
|
|||
logger log.Logger
|
||||
}
|
||||
|
||||
func (mw databaseTracingMiddleware) PluginVersion() (resp logical.PluginVersion) {
|
||||
defer func(then time.Time) {
|
||||
mw.logger.Trace("version",
|
||||
"status", "finished",
|
||||
"version", resp,
|
||||
"took", time.Since(then))
|
||||
}(time.Now())
|
||||
|
||||
mw.logger.Trace("version", "status", "started")
|
||||
if versioner, ok := mw.next.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
func (mw databaseTracingMiddleware) Initialize(ctx context.Context, req InitializeRequest) (resp InitializeResponse, err error) {
|
||||
defer func(then time.Time) {
|
||||
mw.logger.Trace("initialize",
|
||||
|
@ -98,7 +117,10 @@ func (mw databaseTracingMiddleware) Close() (err error) {
|
|||
// Metrics Middleware Domain
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
var _ Database = databaseMetricsMiddleware{}
|
||||
var (
|
||||
_ Database = databaseMetricsMiddleware{}
|
||||
_ logical.PluginVersioner = databaseMetricsMiddleware{}
|
||||
)
|
||||
|
||||
// databaseMetricsMiddleware wraps an implementation of Databases and on
|
||||
// function call logs metrics about this instance.
|
||||
|
@ -108,6 +130,21 @@ type databaseMetricsMiddleware struct {
|
|||
typeStr string
|
||||
}
|
||||
|
||||
func (mw databaseMetricsMiddleware) PluginVersion() logical.PluginVersion {
|
||||
defer func(now time.Time) {
|
||||
metrics.MeasureSince([]string{"database", "PluginVersion"}, now)
|
||||
metrics.MeasureSince([]string{"database", mw.typeStr, "PluginVersion"}, now)
|
||||
}(time.Now())
|
||||
|
||||
metrics.IncrCounter([]string{"database", "PluginVersion"}, 1)
|
||||
metrics.IncrCounter([]string{"database", mw.typeStr, "PluginVersion"}, 1)
|
||||
|
||||
if versioner, ok := mw.next.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
func (mw databaseMetricsMiddleware) Initialize(ctx context.Context, req InitializeRequest) (resp InitializeResponse, err error) {
|
||||
defer func(now time.Time) {
|
||||
metrics.MeasureSince([]string{"database", "Initialize"}, now)
|
||||
|
|
|
@ -4,16 +4,26 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/database/dbplugin/v5/proto"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
var _ logical.PluginVersioner = (*DatabasePluginClient)(nil)
|
||||
|
||||
type DatabasePluginClient struct {
|
||||
client pluginutil.PluginClient
|
||||
Database
|
||||
}
|
||||
|
||||
func (dc *DatabasePluginClient) PluginVersion() logical.PluginVersion {
|
||||
if versioner, ok := dc.Database.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
// This wraps the Close call and ensures we both close the database connection
|
||||
// and kill the plugin.
|
||||
func (dc *DatabasePluginClient) Close() error {
|
||||
|
@ -55,6 +65,7 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, config plug
|
|||
// This is an abstraction leak from go-plugin but it is necessary in
|
||||
// order to enable multiplexing on multiplexed plugins
|
||||
c.client = proto.NewDatabaseClient(pluginClient.Conn())
|
||||
c.versionClient = logical.NewPluginVersionClient(pluginClient.Conn())
|
||||
|
||||
db = c
|
||||
default:
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
@ -38,7 +39,7 @@ func TestNewPluginClient(t *testing.T) {
|
|||
dispenseResp: gRPCClient{client: fakeClient{}},
|
||||
dispenseErr: nil,
|
||||
},
|
||||
Database: gRPCClient{proto.NewDatabaseClient(nil), context.Context(nil)},
|
||||
Database: gRPCClient{client: proto.NewDatabaseClient(nil), versionClient: logical.NewPluginVersionClient(nil), doneCtx: context.Context(nil)},
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
|
|
|
@ -91,6 +91,9 @@ type Backend struct {
|
|||
// BackendType is the logical.BackendType for the backend implementation
|
||||
BackendType logical.BackendType
|
||||
|
||||
// RunningVersion is the optional version that will be self-reported
|
||||
RunningVersion string
|
||||
|
||||
logger log.Logger
|
||||
system logical.SystemView
|
||||
once sync.Once
|
||||
|
@ -428,6 +431,13 @@ func (b *Backend) Type() logical.BackendType {
|
|||
return b.BackendType
|
||||
}
|
||||
|
||||
// Version returns the plugin version information
|
||||
func (b *Backend) PluginVersion() logical.PluginVersion {
|
||||
return logical.PluginVersion{
|
||||
Version: b.RunningVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// Route looks up the path that would be used for a given path string.
|
||||
func (b *Backend) Route(path string) *Path {
|
||||
result, _ := b.route(path)
|
||||
|
|
|
@ -137,3 +137,20 @@ type Auditor interface {
|
|||
AuditRequest(ctx context.Context, input *LogInput) error
|
||||
AuditResponse(ctx context.Context, input *LogInput) error
|
||||
}
|
||||
|
||||
// Externaler allows us to check if a backend is running externally (i.e., over GRPC)
|
||||
type Externaler interface {
|
||||
IsExternal() bool
|
||||
}
|
||||
|
||||
type PluginVersion struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
// PluginVersioner is an optional interface to return version info.
|
||||
type PluginVersioner interface {
|
||||
// PluginVersion returns the version for the backend
|
||||
PluginVersion() PluginVersion
|
||||
}
|
||||
|
||||
var EmptyPluginVersion = PluginVersion{""}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc v3.21.5
|
||||
// source: sdk/logical/version.proto
|
||||
|
||||
package logical
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Empty struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_logical_version_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_logical_version_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_logical_version_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// VersionReply is the reply for the Version method.
|
||||
type VersionReply struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PluginVersion string `protobuf:"bytes,1,opt,name=plugin_version,json=pluginVersion,proto3" json:"plugin_version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *VersionReply) Reset() {
|
||||
*x = VersionReply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_logical_version_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *VersionReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*VersionReply) ProtoMessage() {}
|
||||
|
||||
func (x *VersionReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_logical_version_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use VersionReply.ProtoReflect.Descriptor instead.
|
||||
func (*VersionReply) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_logical_version_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *VersionReply) GetPluginVersion() string {
|
||||
if x != nil {
|
||||
return x.PluginVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_sdk_logical_version_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_sdk_logical_version_proto_rawDesc = []byte{
|
||||
0x0a, 0x19, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x76, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67,
|
||||
0x69, 0x63, 0x61, 0x6c, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x35, 0x0a,
|
||||
0x0c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x25, 0x0a,
|
||||
0x0e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x32, 0x41, 0x0a, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x0e, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
|
||||
0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61,
|
||||
0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_sdk_logical_version_proto_rawDescOnce sync.Once
|
||||
file_sdk_logical_version_proto_rawDescData = file_sdk_logical_version_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_sdk_logical_version_proto_rawDescGZIP() []byte {
|
||||
file_sdk_logical_version_proto_rawDescOnce.Do(func() {
|
||||
file_sdk_logical_version_proto_rawDescData = protoimpl.X.CompressGZIP(file_sdk_logical_version_proto_rawDescData)
|
||||
})
|
||||
return file_sdk_logical_version_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_sdk_logical_version_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_sdk_logical_version_proto_goTypes = []interface{}{
|
||||
(*Empty)(nil), // 0: logical.Empty
|
||||
(*VersionReply)(nil), // 1: logical.VersionReply
|
||||
}
|
||||
var file_sdk_logical_version_proto_depIdxs = []int32{
|
||||
0, // 0: logical.PluginVersion.Version:input_type -> logical.Empty
|
||||
1, // 1: logical.PluginVersion.Version:output_type -> logical.VersionReply
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_sdk_logical_version_proto_init() }
|
||||
func file_sdk_logical_version_proto_init() {
|
||||
if File_sdk_logical_version_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_sdk_logical_version_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_sdk_logical_version_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*VersionReply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_sdk_logical_version_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_sdk_logical_version_proto_goTypes,
|
||||
DependencyIndexes: file_sdk_logical_version_proto_depIdxs,
|
||||
MessageInfos: file_sdk_logical_version_proto_msgTypes,
|
||||
}.Build()
|
||||
File_sdk_logical_version_proto = out.File
|
||||
file_sdk_logical_version_proto_rawDesc = nil
|
||||
file_sdk_logical_version_proto_goTypes = nil
|
||||
file_sdk_logical_version_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
syntax = "proto3";
|
||||
package logical;
|
||||
|
||||
option go_package = "github.com/hashicorp/vault/sdk/logical";
|
||||
|
||||
message Empty {}
|
||||
|
||||
// VersionReply is the reply for the Version method.
|
||||
message VersionReply {
|
||||
string plugin_version = 1;
|
||||
}
|
||||
|
||||
// PluginVersion is an optional RPC service implemented by plugins.
|
||||
service PluginVersion {
|
||||
// Version returns version information for the plugin.
|
||||
rpc Version(Empty) returns (VersionReply);
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package logical
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// PluginVersionClient is the client API for PluginVersion service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type PluginVersionClient interface {
|
||||
// Version returns version information for the plugin.
|
||||
Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionReply, error)
|
||||
}
|
||||
|
||||
type pluginVersionClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewPluginVersionClient(cc grpc.ClientConnInterface) PluginVersionClient {
|
||||
return &pluginVersionClient{cc}
|
||||
}
|
||||
|
||||
func (c *pluginVersionClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionReply, error) {
|
||||
out := new(VersionReply)
|
||||
err := c.cc.Invoke(ctx, "/logical.PluginVersion/Version", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// PluginVersionServer is the server API for PluginVersion service.
|
||||
// All implementations must embed UnimplementedPluginVersionServer
|
||||
// for forward compatibility
|
||||
type PluginVersionServer interface {
|
||||
// Version returns version information for the plugin.
|
||||
Version(context.Context, *Empty) (*VersionReply, error)
|
||||
mustEmbedUnimplementedPluginVersionServer()
|
||||
}
|
||||
|
||||
// UnimplementedPluginVersionServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedPluginVersionServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedPluginVersionServer) Version(context.Context, *Empty) (*VersionReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Version not implemented")
|
||||
}
|
||||
func (UnimplementedPluginVersionServer) mustEmbedUnimplementedPluginVersionServer() {}
|
||||
|
||||
// UnsafePluginVersionServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to PluginVersionServer will
|
||||
// result in compilation errors.
|
||||
type UnsafePluginVersionServer interface {
|
||||
mustEmbedUnimplementedPluginVersionServer()
|
||||
}
|
||||
|
||||
func RegisterPluginVersionServer(s grpc.ServiceRegistrar, srv PluginVersionServer) {
|
||||
s.RegisterService(&PluginVersion_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _PluginVersion_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginVersionServer).Version(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/logical.PluginVersion/Version",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginVersionServer).Version(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// PluginVersion_ServiceDesc is the grpc.ServiceDesc for PluginVersion service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var PluginVersion_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "logical.PluginVersion",
|
||||
HandlerType: (*PluginVersionServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Version",
|
||||
Handler: _PluginVersion_Version_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "sdk/logical/version.proto",
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
"google.golang.org/grpc"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin/pb"
|
||||
|
@ -51,12 +51,14 @@ func (b GRPCBackendPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server)
|
|||
}
|
||||
|
||||
pb.RegisterBackendServer(s, &server)
|
||||
logical.RegisterPluginVersionServer(s, &server)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *GRPCBackendPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||
ret := &backendGRPCPluginClient{
|
||||
client: pb.NewBackendClient(c),
|
||||
versionClient: logical.NewPluginVersionClient(c),
|
||||
clientConn: c,
|
||||
broker: broker,
|
||||
cleanupCh: make(chan struct{}),
|
||||
|
|
|
@ -6,15 +6,14 @@ import (
|
|||
"math"
|
||||
"sync/atomic"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin/pb"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -30,6 +29,7 @@ var _ logical.Backend = &backendGRPCPluginClient{}
|
|||
type backendGRPCPluginClient struct {
|
||||
broker *plugin.GRPCBroker
|
||||
client pb.BackendClient
|
||||
versionClient logical.PluginVersionClient
|
||||
metadataMode bool
|
||||
|
||||
system logical.SystemView
|
||||
|
@ -280,3 +280,23 @@ func (b *backendGRPCPluginClient) Type() logical.BackendType {
|
|||
|
||||
return logical.BackendType(reply.Type)
|
||||
}
|
||||
|
||||
func (b *backendGRPCPluginClient) PluginVersion() logical.PluginVersion {
|
||||
reply, err := b.versionClient.Version(b.doneCtx, &logical.Empty{})
|
||||
if err != nil {
|
||||
if stErr, ok := status.FromError(err); ok {
|
||||
if stErr.Code() == codes.Unimplemented {
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
}
|
||||
b.Logger().Warn("Unknown error getting plugin version", "err", err)
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
return logical.PluginVersion{
|
||||
Version: reply.GetPluginVersion(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backendGRPCPluginClient) IsExternal() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin/pb"
|
||||
|
@ -27,6 +27,7 @@ type backendInstance struct {
|
|||
|
||||
type backendGRPCPluginServer struct {
|
||||
pb.UnimplementedBackendServer
|
||||
logical.UnimplementedPluginVersionServer
|
||||
|
||||
broker *plugin.GRPCBroker
|
||||
|
||||
|
@ -266,3 +267,19 @@ func (b *backendGRPCPluginServer) Type(ctx context.Context, _ *pb.Empty) (*pb.Ty
|
|||
Type: uint32(backend.Type()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *backendGRPCPluginServer) Version(ctx context.Context, _ *logical.Empty) (*logical.VersionReply, error) {
|
||||
backend, _, err := b.getBackendAndBrokeredClient(ctx)
|
||||
if err != nil {
|
||||
return &logical.VersionReply{}, err
|
||||
}
|
||||
|
||||
if versioner, ok := backend.(logical.PluginVersioner); ok {
|
||||
return &logical.VersionReply{
|
||||
PluginVersion: versioner.PluginVersion().Version,
|
||||
}, nil
|
||||
}
|
||||
return &logical.VersionReply{
|
||||
PluginVersion: "",
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -147,6 +147,21 @@ func TestGRPCBackendPlugin_Initialize(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGRPCBackendPlugin_Version(t *testing.T) {
|
||||
b, cleanup := testGRPCBackend(t)
|
||||
defer cleanup()
|
||||
|
||||
versioner, ok := b.(logical.PluginVersioner)
|
||||
if !ok {
|
||||
t.Fatalf("Expected %T to implement logical.PluginVersioner interface", b)
|
||||
}
|
||||
|
||||
version := versioner.PluginVersion().Version
|
||||
if version != "mock" {
|
||||
t.Fatalf("Got version %s, expected 'mock'", version)
|
||||
}
|
||||
}
|
||||
|
||||
func testGRPCBackend(t *testing.T) (logical.Backend, func()) {
|
||||
// Create a mock provider
|
||||
pluginMap := map[string]gplugin.Plugin{
|
||||
|
|
|
@ -98,3 +98,15 @@ func (b *BackendTracingMiddleware) Type() logical.BackendType {
|
|||
b.logger.Trace("type", "status", "started")
|
||||
return b.next.Type()
|
||||
}
|
||||
|
||||
func (b *BackendTracingMiddleware) PluginVersion() logical.PluginVersion {
|
||||
defer func(then time.Time) {
|
||||
b.logger.Trace("version", "status", "finished", "took", time.Since(then))
|
||||
}(time.Now())
|
||||
|
||||
b.logger.Trace("version", "status", "started")
|
||||
if versioner, ok := b.next.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ func Backend() *backend {
|
|||
BackendType: logical.TypeLogical,
|
||||
}
|
||||
b.internal = "bar"
|
||||
b.RunningVersion = "mock"
|
||||
return &b
|
||||
}
|
||||
|
||||
|
|
|
@ -143,3 +143,22 @@ func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunne
|
|||
Backend: backend,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *BackendPluginClient) PluginVersion() logical.PluginVersion {
|
||||
if versioner, ok := b.Backend.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
func (b *BackendPluginClient) IsExternal() bool {
|
||||
if externaler, ok := b.Backend.(logical.Externaler); ok {
|
||||
return externaler.IsExternal()
|
||||
}
|
||||
return true // default to true since this is only used for GRPC plugins
|
||||
}
|
||||
|
||||
var (
|
||||
_ logical.PluginVersioner = (*BackendPluginClient)(nil)
|
||||
_ logical.Externaler = (*BackendPluginClient)(nil)
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
|
@ -41,6 +41,22 @@ func (b *BackendPluginClientV5) Cleanup(ctx context.Context) {
|
|||
b.client.Reload()
|
||||
}
|
||||
|
||||
func (b *BackendPluginClientV5) IsExternal() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *BackendPluginClientV5) PluginVersion() logical.PluginVersion {
|
||||
if versioner, ok := b.Backend.(logical.PluginVersioner); ok {
|
||||
return versioner.PluginVersion()
|
||||
}
|
||||
return logical.EmptyPluginVersion
|
||||
}
|
||||
|
||||
var (
|
||||
_ logical.PluginVersioner = (*BackendPluginClientV5)(nil)
|
||||
_ logical.Externaler = (*BackendPluginClientV5)(nil)
|
||||
)
|
||||
|
||||
// NewBackendV5 will return an instance of an RPC-based client implementation of
|
||||
// the backend for external plugins, or a concrete implementation of the
|
||||
// backend if it is a builtin backend. The backend is returned as a
|
||||
|
@ -111,6 +127,7 @@ func Dispense(rpcClient plugin.ClientProtocol, pluginClient pluginutil.PluginCli
|
|||
// This is an abstraction leak from go-plugin but it is necessary in
|
||||
// order to enable multiplexing on multiplexed plugins
|
||||
c.client = pb.NewBackendClient(pluginClient.Conn())
|
||||
c.versionClient = logical.NewPluginVersionClient(pluginClient.Conn())
|
||||
|
||||
backend = c
|
||||
default:
|
||||
|
@ -144,6 +161,7 @@ func NewPluginClientV5(ctx context.Context, sys pluginutil.RunnerUtil, config pl
|
|||
// This is an abstraction leak from go-plugin but it is necessary in
|
||||
// order to enable multiplexing on multiplexed plugins
|
||||
c.client = pb.NewBackendClient(pluginClient.Conn())
|
||||
c.versionClient = logical.NewPluginVersionClient(pluginClient.Conn())
|
||||
|
||||
backend = c
|
||||
transport = "gRPC"
|
||||
|
|
|
@ -7,9 +7,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/builtin/plugin"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
|
@ -182,7 +183,16 @@ func (c *Core) enableCredentialInternal(ctx context.Context, entry *MountEntry,
|
|||
if backendType != logical.TypeCredential {
|
||||
return fmt.Errorf("cannot mount %q of type %q as an auth backend", entry.Type, backendType)
|
||||
}
|
||||
|
||||
// update the entry running version with the backend's reported version
|
||||
if versioner, ok := backend.(logical.PluginVersioner); ok {
|
||||
entry.RunningVersion = versioner.PluginVersion().Version
|
||||
}
|
||||
if entry.RunningVersion == "" {
|
||||
// don't set the running version to a builtin if it is running as an external plugin
|
||||
if externaler, ok := backend.(logical.Externaler); !ok || !externaler.IsExternal() {
|
||||
entry.RunningVersion = versions.GetBuiltinVersion(consts.PluginTypeCredential, entry.Type)
|
||||
}
|
||||
}
|
||||
addPathCheckers(c, entry, backend, viewPath)
|
||||
|
||||
// If the mount is filtered or we are on a DR secondary we don't want to
|
||||
|
|
|
@ -242,6 +242,11 @@ func TestCore_EnableExternalPlugin_MultipleVersions(t *testing.T) {
|
|||
if raw.(*routeEntry).mountEntry.Version != tc.mountVersion {
|
||||
t.Errorf("Expected mount to be version %s but got %s", tc.mountVersion, raw.(*routeEntry).mountEntry.Version)
|
||||
}
|
||||
|
||||
// we don't override the running version of non-builtins, and they don't have the version set explicitly (yet)
|
||||
if raw.(*routeEntry).mountEntry.RunningVersion != "" {
|
||||
t.Errorf("Expected mount to have no running version but got %s", raw.(*routeEntry).mountEntry.RunningVersion)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/storagepacker"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
|
@ -112,6 +113,7 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo
|
|||
|
||||
return nil
|
||||
},
|
||||
RunningVersion: versions.DefaultBuiltinVersion,
|
||||
}
|
||||
|
||||
iStore.oidcCache = newOIDCCache(cache.NoExpiration, cache.NoExpiration)
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
@ -16,6 +18,7 @@ func CubbyholeBackendFactory(ctx context.Context, conf *logical.BackendConfig) (
|
|||
b := &CubbyholeBackend{}
|
||||
b.Backend = &framework.Backend{
|
||||
Help: strings.TrimSpace(cubbyholeHelp),
|
||||
RunningVersion: versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
}
|
||||
|
||||
b.Backend.Paths = append(b.Backend.Paths, b.paths()...)
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
|
@ -84,6 +85,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend {
|
|||
}
|
||||
|
||||
b.Backend = &framework.Backend{
|
||||
RunningVersion: versions.DefaultBuiltinVersion,
|
||||
Help: strings.TrimSpace(sysHelpRoot),
|
||||
|
||||
PathsSpecial: &logical.Paths{
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/random"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/compressutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
|
@ -175,7 +176,7 @@ func TestSystemBackend_mounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
"type": "system",
|
||||
|
@ -195,7 +196,7 @@ func TestSystemBackend_mounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
"description": "per-token private secret storage",
|
||||
|
@ -214,7 +215,7 @@ func TestSystemBackend_mounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
"description": "identity store",
|
||||
|
@ -234,7 +235,7 @@ func TestSystemBackend_mounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
},
|
||||
}
|
||||
if diff := deep.Equal(resp.Data, exp); len(diff) > 0 {
|
||||
|
@ -304,7 +305,7 @@ func TestSystemBackend_mount(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
"type": "system",
|
||||
|
@ -324,7 +325,7 @@ func TestSystemBackend_mount(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
"description": "per-token private secret storage",
|
||||
|
@ -343,7 +344,7 @@ func TestSystemBackend_mount(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
"description": "identity store",
|
||||
|
@ -363,7 +364,7 @@ func TestSystemBackend_mount(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
},
|
||||
"prod/secret/": map[string]interface{}{
|
||||
"description": "",
|
||||
|
@ -384,7 +385,7 @@ func TestSystemBackend_mount(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
},
|
||||
}
|
||||
if diff := deep.Equal(resp.Data, exp); len(diff) > 0 {
|
||||
|
@ -1928,7 +1929,7 @@ func TestSystemBackend_enableAuth(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
},
|
||||
"token/": map[string]interface{}{
|
||||
"type": "token",
|
||||
|
@ -2999,7 +3000,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) {
|
|||
"args": []string(nil),
|
||||
"sha256": "",
|
||||
"builtin": true,
|
||||
"version": c.pluginCatalog.getBuiltinVersion(consts.PluginTypeDatabase, "mysql-database-plugin"),
|
||||
"version": versions.GetBuiltinVersion(consts.PluginTypeDatabase, "mysql-database-plugin"),
|
||||
"deprecation_status": deprecationStatus.String(),
|
||||
}
|
||||
if !reflect.DeepEqual(actualRespData, expectedRespData) {
|
||||
|
@ -3329,7 +3330,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
},
|
||||
"sys/": map[string]interface{}{
|
||||
"type": "system",
|
||||
|
@ -3349,7 +3350,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.DefaultBuiltinVersion,
|
||||
},
|
||||
"cubbyhole/": map[string]interface{}{
|
||||
"description": "per-token private secret storage",
|
||||
|
@ -3368,7 +3369,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
},
|
||||
"identity/": map[string]interface{}{
|
||||
"description": "identity store",
|
||||
|
@ -3388,7 +3389,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
|||
"sha": "",
|
||||
"running_sha": "",
|
||||
"version": "",
|
||||
"running_version": "",
|
||||
"running_version": versions.GetBuiltinVersion(consts.PluginTypeSecrets, "identity"),
|
||||
},
|
||||
},
|
||||
"auth": map[string]interface{}{
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/hashicorp/vault/builtin/plugin"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
|
@ -624,6 +625,17 @@ func (c *Core) mountInternal(ctx context.Context, entry *MountEntry, updateStora
|
|||
}
|
||||
}
|
||||
|
||||
// update the entry running version with the backend's reported version
|
||||
if versioner, ok := backend.(logical.PluginVersioner); ok {
|
||||
entry.RunningVersion = versioner.PluginVersion().Version
|
||||
}
|
||||
if entry.RunningVersion == "" {
|
||||
// don't set the running version to a builtin if it is running as an external plugin
|
||||
if externaler, ok := backend.(logical.Externaler); !ok || !externaler.IsExternal() {
|
||||
entry.RunningVersion = versions.GetBuiltinVersion(consts.PluginTypeSecrets, entry.Type)
|
||||
}
|
||||
}
|
||||
|
||||
addPathCheckers(c, entry, backend, viewPath)
|
||||
|
||||
c.setCoreBackend(entry, backend, view)
|
||||
|
@ -1609,6 +1621,7 @@ func (c *Core) defaultMountTable() *MountTable {
|
|||
Options: map[string]string{
|
||||
"version": "2",
|
||||
},
|
||||
RunningVersion: versions.GetBuiltinVersion(consts.PluginTypeSecrets, "kv"),
|
||||
}
|
||||
table.Entries = append(table.Entries, kvMount)
|
||||
}
|
||||
|
@ -1643,6 +1656,7 @@ func (c *Core) requiredMountTable() *MountTable {
|
|||
Accessor: cubbyholeAccessor,
|
||||
Local: true,
|
||||
BackendAwareUUID: cubbyholeBackendUUID,
|
||||
RunningVersion: versions.GetBuiltinVersion(consts.PluginTypeSecrets, "cubbyhole"),
|
||||
}
|
||||
|
||||
sysUUID, err := uuid.GenerateUUID()
|
||||
|
@ -1669,6 +1683,7 @@ func (c *Core) requiredMountTable() *MountTable {
|
|||
Config: MountConfig{
|
||||
PassthroughRequestHeaders: []string{"Accept"},
|
||||
},
|
||||
RunningVersion: versions.DefaultBuiltinVersion,
|
||||
}
|
||||
|
||||
identityUUID, err := uuid.GenerateUUID()
|
||||
|
@ -1694,6 +1709,7 @@ func (c *Core) requiredMountTable() *MountTable {
|
|||
Config: MountConfig{
|
||||
PassthroughRequestHeaders: []string{"Authorization"},
|
||||
},
|
||||
RunningVersion: versions.DefaultBuiltinVersion,
|
||||
}
|
||||
|
||||
table.Entries = append(table.Entries, cubbyholeMount)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
"github.com/hashicorp/vault/helper/metricsutil"
|
||||
|
@ -187,6 +187,30 @@ func TestCore_Mount(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCore_Mount_secrets_builtin_RunningVersion(t *testing.T) {
|
||||
c, _, _ := TestCoreUnsealed(t)
|
||||
me := &MountEntry{
|
||||
Table: mountTableType,
|
||||
Path: "foo",
|
||||
Type: "generic",
|
||||
}
|
||||
err := c.mount(namespace.RootContext(nil), me)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
match := c.router.MatchingMount(namespace.RootContext(nil), "foo/bar")
|
||||
if match != "foo/" {
|
||||
t.Fatalf("missing mount")
|
||||
}
|
||||
|
||||
raw, _ := c.router.root.Get(match)
|
||||
// we override the running version of builtins
|
||||
if !strings.Contains(raw.(*routeEntry).mountEntry.RunningVersion, "builtin") {
|
||||
t.Errorf("Expected mount to have builtin version but got %s", raw.(*routeEntry).mountEntry.RunningVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCore_Mount_kv_generic tests that we can successfully mount kv using the
|
||||
// kv alias "generic"
|
||||
func TestCore_Mount_kv_generic(t *testing.T) {
|
||||
|
|
|
@ -8,15 +8,15 @@ import (
|
|||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/go-secure-stdlib/base62"
|
||||
semver "github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
|
||||
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
backendplugin "github.com/hashicorp/vault/sdk/plugin"
|
||||
"github.com/hashicorp/vault/sdk/version"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
@ -55,10 +54,6 @@ type PluginCatalog struct {
|
|||
externalPlugins map[string]*externalPlugin
|
||||
mlockPlugins bool
|
||||
|
||||
// once is used to ensure we only parse build info once.
|
||||
once sync.Once
|
||||
buildInfo *debug.BuildInfo
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
|
@ -630,7 +625,7 @@ func (c *PluginCatalog) get(ctx context.Context, name string, pluginType consts.
|
|||
Type: pluginType,
|
||||
Builtin: true,
|
||||
BuiltinFactory: factory,
|
||||
Version: c.getBuiltinVersion(pluginType, name),
|
||||
Version: versions.GetBuiltinVersion(pluginType, name),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
@ -851,7 +846,7 @@ func (c *PluginCatalog) listInternal(ctx context.Context, pluginType consts.Plug
|
|||
continue
|
||||
}
|
||||
|
||||
version := c.getBuiltinVersion(pluginType, plugin)
|
||||
version := versions.GetBuiltinVersion(pluginType, plugin)
|
||||
semanticVersion, err := semver.NewVersion(version)
|
||||
deprecationStatus, _ := c.builtinRegistry.DeprecationStatus(plugin, pluginType)
|
||||
if err != nil {
|
||||
|
@ -874,42 +869,3 @@ func isPluginType(s string) bool {
|
|||
_, err := consts.ParsePluginType(s)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (c *PluginCatalog) getBuiltinVersion(pluginType consts.PluginType, pluginName string) string {
|
||||
defaultBuiltinVersion := "v" + version.GetVersion().Version + "+builtin.vault"
|
||||
|
||||
c.once.Do(func() {
|
||||
c.buildInfo, _ = debug.ReadBuildInfo()
|
||||
})
|
||||
|
||||
// Should never happen, means the binary was built without Go modules.
|
||||
// Fall back to just the Vault version.
|
||||
if c.buildInfo == nil {
|
||||
return defaultBuiltinVersion
|
||||
}
|
||||
|
||||
// Vault builtin plugins are all either:
|
||||
// a) An external repo within the hashicorp org - return external repo version with +builtin
|
||||
// b) Within the Vault repo itself - return Vault version with +builtin.vault
|
||||
//
|
||||
// The repo names are predictable, but follow slightly different patterns
|
||||
// for each plugin type.
|
||||
t := pluginType.String()
|
||||
switch pluginType {
|
||||
case consts.PluginTypeDatabase:
|
||||
// Database plugin built-ins are registered as e.g. "postgresql-database-plugin"
|
||||
pluginName = strings.TrimSuffix(pluginName, "-database-plugin")
|
||||
case consts.PluginTypeSecrets:
|
||||
// Repos use "secrets", pluginType.String() is "secret".
|
||||
t = "secrets"
|
||||
}
|
||||
pluginModulePath := fmt.Sprintf("github.com/hashicorp/vault-plugin-%s-%s", t, pluginName)
|
||||
|
||||
for _, dep := range c.buildInfo.Deps {
|
||||
if dep.Path == pluginModulePath {
|
||||
return dep.Version + "+builtin"
|
||||
}
|
||||
}
|
||||
|
||||
return defaultBuiltinVersion
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/builtin/credential/userpass"
|
||||
"github.com/hashicorp/vault/helper/versions"
|
||||
"github.com/hashicorp/vault/plugins/database/postgresql"
|
||||
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
|
@ -41,7 +42,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||
Name: pluginName,
|
||||
Type: consts.PluginTypeDatabase,
|
||||
Builtin: true,
|
||||
Version: core.pluginCatalog.getBuiltinVersion(consts.PluginTypeDatabase, pluginName),
|
||||
Version: versions.GetBuiltinVersion(consts.PluginTypeDatabase, pluginName),
|
||||
}
|
||||
expectedBuiltin.BuiltinFactory, _ = builtinplugins.Registry.Get(pluginName, consts.PluginTypeDatabase)
|
||||
|
||||
|
@ -104,7 +105,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||
Name: pluginName,
|
||||
Type: consts.PluginTypeDatabase,
|
||||
Builtin: true,
|
||||
Version: core.pluginCatalog.getBuiltinVersion(consts.PluginTypeDatabase, pluginName),
|
||||
Version: versions.GetBuiltinVersion(consts.PluginTypeDatabase, pluginName),
|
||||
}
|
||||
expectedBuiltin.BuiltinFactory, _ = builtinplugins.Registry.Get(pluginName, consts.PluginTypeDatabase)
|
||||
|
||||
|
|
Loading…
Reference in New Issue