-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Add plugin version to GRPC interface #17088
Conversation
d590e4b
to
0886a5d
Compare
It looks like a lot of the failed tests are using the PassthroughBackend, so I wonder if that needs another type assertion in request_handling.go and the factory wrapped in request_handling_test.go: diff --git a/vault/request_handling.go b/vault/request_handling.go
index b37085c83c..95243bb38a 100644
--- a/vault/request_handling.go
+++ b/vault/request_handling.go
@@ -1076,7 +1076,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
return nil, auth, retErr
}
- if ptbe, ok := matchingBackend.(*PassthroughBackend); ok {
+ if ptbe, ok := matchingBackend.(builtinVersionBackend).backend.(*PassthroughBackend); ok {
if !ptbe.GeneratesLeases() {
registerLease = false
resp.Secret.Renewable = false
diff --git a/vault/request_handling_test.go b/vault/request_handling_test.go
index cfdea525f7..b79601039b 100644
--- a/vault/request_handling_test.go
+++ b/vault/request_handling_test.go
@@ -17,7 +17,7 @@ import (
func TestRequestHandling_Wrapping(t *testing.T) {
core, _, root := TestCoreUnsealed(t)
- core.logicalBackends["kv"] = PassthroughBackendFactory
+ core.logicalBackends["kv"] = wrapFactoryAddBuiltinVersion(PassthroughBackendFactory)
meUUID, _ := uuid.GenerateUUID()
err := core.mount(namespace.RootContext(nil), &MountEntry{ |
Thanks @tvoran for the tip about the passthroughbackend -- that was exactly the problem. |
I converted this into a draft since I have a bit more work to do before it's quite ready. I went ahead and removed the plugin wrapper and used the build information to get the plugin version, as Tom suggested, which simplifies things a little. |
Unlike the POC, I went ahead and added it into the main interface. So far in my testing, this has been safe to do. I also took a slightly different approach for how to include the `v1.12.0+builtin` version in the builtin plugins: I added a new wrapper struct that sets the version to the "builtin" version if it isn't already set, and I wrap all of the builtin factories with that when we create the map of factories. This way we don't have to update every builtin plugin to add the version logic. If we do want to version those plugins separately, though, we will be able to do so. There are still some TODOs before merging this, I feel, but this is getting large so I wanted to check in: * [ ] More manual testing with existing plugins and updated (with version) ones * [ ] Update identity/cubbyhole/sys/etc. special backends to support version method.
…since we won't need it anymore
34ec9ba
to
4626aa8
Compare
6e99f04
to
fdf2894
Compare
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.
@@ -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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this function to more central place so it could be used more easily. It didn't really need access to the pluginCatalog
to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! It might be that my comment about validating the self-reported version during registration would be best left for another PR if that's non-trivial
// don't override a manually specified plugin version | ||
// but if it is empty, then add in the self-reported version for informational purposes | ||
if config.PluginVersion == "" { | ||
config.PluginVersion = dbw.Version().Version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could cause issues the next time the config is updated if it's different to the version we did the initial lookup with, as next time we'll lookup the reported version and maybe get a different entry from the catalog. Could we call this method as we're registering the plugin, and either a) Use the reported version in the catalog if none was provided or b) Error if the reported version is different from the one provided? Then we shouldn't need to make any updates here as the catalog and reported versions will always match up.
It might be interesting to query the version from the plugin and validate that it's the version we think it should be or error out, though I can't think of any reason that would fail if we validate during plugin registration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is definitely possible to add a check between PluginVersion
and the self-reported version on registration.
I primarily added this because, if no version is specified on registration, it is otherwise impossible to tell what version of the plugin is running, and I wanted to expose this somewhere.
It doesn't even really make sense to have this information in the config at all, but there's no mount or auth table for database configs.
b.Backend = &framework.Backend{ | ||
PathsSpecial: paths, | ||
BackendType: btype, | ||
b.Backend = &placeholderBackend{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
placeholderBackend.IsExternal()
always returns true, but the function comment for Backend
implies the backend could be builtin. Not sure if I'm just misreading though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you're right, the placeholder is used even for builtin plugins. I'll see if I can just forward the IsExternal
method.
sdk/logical/version.proto
Outdated
} | ||
|
||
// Versioned is an optional RPC service implemented by plugins. | ||
service Versioned { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT about PluginVersion
to differentiate from Vault version and protocol version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
sdk/logical/version.proto
Outdated
// VersionReply is the reply for the Version method. | ||
message VersionReply { | ||
string plugin_name = 1; | ||
string version = 2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally wanted to use version everywhere, so started sprinkling that around. But then we've found a couple of places where we need to use plugin_version to avoid ambiguity. I'm wondering if we should standardise on plugin_version everywhere for consistency, and for an abundance of clarity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that makes sense. I'll rename it.
return logical.EmptyVersion | ||
} | ||
} | ||
b.Logger().Warn("Unknown error getting plugin version", "err", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something would have to go seriously wrong for this to happen, which makes me a bit nervous to only log it server-side and continue. I see Type
does something similar above, but perhaps it's worth adding an error return value to our new interface signature before we first ship it so that we have an easier time if we have need for it at a later date. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to mimic Type
's relative safety -- worst case scenario, the Version
will just return ""
. I can think of few reasons why this might fail, like if the plugin dies or is running too slowly.
(Type
won't even log a warning if an error occurs. :))
I'm open to adding an error return though; I was just trying to keep it as simple as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's definitely nice to have that simplicity. I was thinking a little of possible future changes to the plugin architecture, e.g. what if we wanted to support remote plugins where transport errors might become less unusual. It's perfectly possible the error will never be a particularly useful part of the API though, so I'm not 100% about it.
* Change changelog to improvement * Make IsExternal configurable for plugin placeholder * Start changing version to pluginVersion everywhere
7e7581a
to
0e6d9e6
Compare
@tomhjp I think I addressed your concerns. PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple naive questions.
Thanks! |
Added a version interface in the
sdk/logical
so that it can be shared between all plugin types, and then wired it up toRunningVersion
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
:Version()
method implemented, then that is usedgo.mod
version is usedMy apologies for the length of this PR.