mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	Refactor plugin catalog set functions (#22666)
Use a struct arg instead of a long list of args. Plugins running in containers will require even more args and it's getting difficult to maintain.
This commit is contained in:
		| @@ -61,6 +61,20 @@ type PluginRunner struct { | ||||
| 	BuiltinFactory func() (interface{}, error) `json:"-" structs:"-"` | ||||
| } | ||||
|  | ||||
| // SetPluginInput is only used as input for the plugin catalog's set methods. | ||||
| // We don't use the very similar PluginRunner struct to avoid confusion about | ||||
| // what's settable, which does not include the builtin fields. | ||||
| type SetPluginInput struct { | ||||
| 	Name     string | ||||
| 	Type     consts.PluginType | ||||
| 	Version  string | ||||
| 	Command  string | ||||
| 	OCIImage string | ||||
| 	Args     []string | ||||
| 	Env      []string | ||||
| 	Sha256   []byte | ||||
| } | ||||
|  | ||||
| // Run takes a wrapper RunnerUtil instance along with the go-plugin parameters and | ||||
| // returns a configured plugin.Client with TLS Configured and a wrapping token set | ||||
| // on PluginUnwrapTokenEnv for plugin process consumption. | ||||
|   | ||||
| @@ -556,7 +556,15 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, _ *logica | ||||
| 		return logical.ErrorResponse("Could not decode SHA-256 value from Hex"), err | ||||
| 	} | ||||
|  | ||||
| 	err = b.Core.pluginCatalog.Set(ctx, pluginName, pluginType, pluginVersion, parts[0], args, env, sha256Bytes) | ||||
| 	err = b.Core.pluginCatalog.Set(ctx, pluginutil.SetPluginInput{ | ||||
| 		Name:    pluginName, | ||||
| 		Type:    pluginType, | ||||
| 		Version: pluginVersion, | ||||
| 		Command: parts[0], | ||||
| 		Args:    args, | ||||
| 		Env:     env, | ||||
| 		Sha256:  sha256Bytes, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, ErrPluginNotFound) || strings.HasPrefix(err.Error(), "plugin version mismatch") { | ||||
| 			return logical.ErrorResponse(err.Error()), nil | ||||
|   | ||||
| @@ -2189,7 +2189,15 @@ func TestSystemBackend_tuneAuth(t *testing.T) { | ||||
| 		if err := file.Close(); err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
| 		err = c.pluginCatalog.Set(context.Background(), "token", consts.PluginTypeCredential, "v1.0.0", "foo", []string{}, []string{}, []byte{}) | ||||
| 		err = c.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 			Name:    "token", | ||||
| 			Type:    consts.PluginTypeCredential, | ||||
| 			Version: "v1.0.0", | ||||
| 			Command: "foo", | ||||
| 			Args:    []string{}, | ||||
| 			Env:     []string{}, | ||||
| 			Sha256:  []byte{}, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
| @@ -5742,7 +5750,15 @@ func TestValidateVersion_HelpfulErrorWhenBuiltinOverridden(t *testing.T) { | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	command := filepath.Base(file.Name()) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), "kubernetes", consts.PluginTypeCredential, "", command, nil, nil, nil) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    "kubernetes", | ||||
| 		Type:    consts.PluginTypeCredential, | ||||
| 		Version: "", | ||||
| 		Command: command, | ||||
| 		Args:    nil, | ||||
| 		Env:     nil, | ||||
| 		Sha256:  nil, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|   | ||||
| @@ -773,7 +773,15 @@ func (c *PluginCatalog) UpgradePlugins(ctx context.Context, logger log.Logger) e | ||||
| 		plugin.Command = filepath.Join(c.directory, plugin.Command) | ||||
|  | ||||
| 		// Upgrade the storage. At this point we don't know what type of plugin this is so pass in the unknown type. | ||||
| 		runner, err := c.setInternal(ctx, pluginName, consts.PluginTypeUnknown, plugin.Version, cmdOld, plugin.Args, plugin.Env, plugin.Sha256) | ||||
| 		runner, err := c.setInternal(ctx, pluginutil.SetPluginInput{ | ||||
| 			Name:    pluginName, | ||||
| 			Type:    consts.PluginTypeUnknown, | ||||
| 			Version: plugin.Version, | ||||
| 			Command: cmdOld, | ||||
| 			Args:    plugin.Args, | ||||
| 			Env:     plugin.Env, | ||||
| 			Sha256:  plugin.Sha256, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			if errors.Is(err, ErrPluginBadType) { | ||||
| 				retErr = multierror.Append(retErr, fmt.Errorf("could not upgrade plugin %s: plugin of unknown type", pluginName)) | ||||
| @@ -868,29 +876,29 @@ func (c *PluginCatalog) get(ctx context.Context, name string, pluginType consts. | ||||
|  | ||||
| // Set registers a new external plugin with the catalog, or updates an existing | ||||
| // external plugin. It takes the name, command and SHA256 of the plugin. | ||||
| func (c *PluginCatalog) Set(ctx context.Context, name string, pluginType consts.PluginType, version string, command string, args []string, env []string, sha256 []byte) error { | ||||
| func (c *PluginCatalog) Set(ctx context.Context, plugin pluginutil.SetPluginInput) error { | ||||
| 	if c.directory == "" { | ||||
| 		return ErrDirectoryNotConfigured | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case strings.Contains(name, ".."): | ||||
| 	case strings.Contains(plugin.Name, ".."): | ||||
| 		fallthrough | ||||
| 	case strings.Contains(command, ".."): | ||||
| 	case strings.Contains(plugin.Command, ".."): | ||||
| 		return consts.ErrPathContainsParentReferences | ||||
| 	} | ||||
|  | ||||
| 	c.lock.Lock() | ||||
| 	defer c.lock.Unlock() | ||||
|  | ||||
| 	_, err := c.setInternal(ctx, name, pluginType, version, command, args, env, sha256) | ||||
| 	_, err := c.setInternal(ctx, plugin) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (c *PluginCatalog) setInternal(ctx context.Context, name string, pluginType consts.PluginType, version string, command string, args []string, env []string, sha256 []byte) (*pluginutil.PluginRunner, error) { | ||||
| func (c *PluginCatalog) setInternal(ctx context.Context, plugin pluginutil.SetPluginInput) (*pluginutil.PluginRunner, error) { | ||||
| 	// Best effort check to make sure the command isn't breaking out of the | ||||
| 	// configured plugin directory. | ||||
| 	commandFull := filepath.Join(c.directory, command) | ||||
| 	commandFull := filepath.Join(c.directory, plugin.Command) | ||||
| 	sym, err := filepath.EvalSymlinks(commandFull) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error while validating the command path: %w", err) | ||||
| @@ -907,20 +915,21 @@ func (c *PluginCatalog) setInternal(ctx context.Context, name string, pluginType | ||||
| 	// entryTmp should only be used for the below type and version checks, it uses the | ||||
| 	// full command instead of the relative command. | ||||
| 	entryTmp := &pluginutil.PluginRunner{ | ||||
| 		Name:    name, | ||||
| 		Name:    plugin.Name, | ||||
| 		Command: commandFull, | ||||
| 		Args:    args, | ||||
| 		Env:     env, | ||||
| 		Sha256:  sha256, | ||||
| 		Args:    plugin.Args, | ||||
| 		Env:     plugin.Env, | ||||
| 		Sha256:  plugin.Sha256, | ||||
| 		Builtin: false, | ||||
| 	} | ||||
| 	// If the plugin type is unknown, we want to attempt to determine the type | ||||
| 	if pluginType == consts.PluginTypeUnknown { | ||||
| 		pluginType, err = c.getPluginTypeFromUnknown(ctx, entryTmp) | ||||
| 	if plugin.Type == consts.PluginTypeUnknown { | ||||
| 		var err error | ||||
| 		plugin.Type, err = c.getPluginTypeFromUnknown(ctx, entryTmp) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if pluginType == consts.PluginTypeUnknown { | ||||
| 		if plugin.Type == consts.PluginTypeUnknown { | ||||
| 			return nil, ErrPluginBadType | ||||
| 		} | ||||
| 	} | ||||
| @@ -928,36 +937,36 @@ func (c *PluginCatalog) setInternal(ctx context.Context, name string, pluginType | ||||
| 	// getting the plugin version is best-effort, so errors are not fatal | ||||
| 	runningVersion := logical.EmptyPluginVersion | ||||
| 	var versionErr error | ||||
| 	switch pluginType { | ||||
| 	switch plugin.Type { | ||||
| 	case consts.PluginTypeSecrets, consts.PluginTypeCredential: | ||||
| 		runningVersion, versionErr = c.getBackendRunningVersion(ctx, entryTmp) | ||||
| 	case consts.PluginTypeDatabase: | ||||
| 		runningVersion, versionErr = c.getDatabaseRunningVersion(ctx, entryTmp) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unknown plugin type: %v", pluginType) | ||||
| 		return nil, fmt.Errorf("unknown plugin type: %v", plugin.Type) | ||||
| 	} | ||||
| 	if versionErr != nil { | ||||
| 		c.logger.Warn("Error determining plugin version", "error", versionErr) | ||||
| 	} else if version != "" && runningVersion.Version != "" && version != runningVersion.Version { | ||||
| 		c.logger.Warn("Plugin self-reported version did not match requested version", "plugin", name, "requestedVersion", version, "reportedVersion", runningVersion.Version) | ||||
| 		return nil, fmt.Errorf("plugin version mismatch: %s reported version (%s) did not match requested version (%s)", name, runningVersion.Version, version) | ||||
| 	} else if version == "" && runningVersion.Version != "" { | ||||
| 		version = runningVersion.Version | ||||
| 		_, err := semver.NewVersion(version) | ||||
| 	} else if plugin.Version != "" && runningVersion.Version != "" && plugin.Version != runningVersion.Version { | ||||
| 		c.logger.Warn("Plugin self-reported version did not match requested version", "plugin", plugin.Name, "requestedVersion", plugin.Version, "reportedVersion", runningVersion.Version) | ||||
| 		return nil, fmt.Errorf("plugin version mismatch: %s reported version (%s) did not match requested version (%s)", plugin.Name, runningVersion.Version, plugin.Version) | ||||
| 	} else if plugin.Version == "" && runningVersion.Version != "" { | ||||
| 		plugin.Version = runningVersion.Version | ||||
| 		_, err := semver.NewVersion(plugin.Version) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("plugin self-reported version %q is not a valid semantic version: %w", version, err) | ||||
| 			return nil, fmt.Errorf("plugin self-reported version %q is not a valid semantic version: %w", plugin.Version, err) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	entry := &pluginutil.PluginRunner{ | ||||
| 		Name:    name, | ||||
| 		Type:    pluginType, | ||||
| 		Version: version, | ||||
| 		Command: command, | ||||
| 		Args:    args, | ||||
| 		Env:     env, | ||||
| 		Sha256:  sha256, | ||||
| 		Name:    plugin.Name, | ||||
| 		Type:    plugin.Type, | ||||
| 		Version: plugin.Version, | ||||
| 		Command: plugin.Command, | ||||
| 		Args:    plugin.Args, | ||||
| 		Env:     plugin.Env, | ||||
| 		Sha256:  plugin.Sha256, | ||||
| 		Builtin: false, | ||||
| 	} | ||||
|  | ||||
| @@ -966,9 +975,9 @@ func (c *PluginCatalog) setInternal(ctx context.Context, name string, pluginType | ||||
| 		return nil, fmt.Errorf("failed to encode plugin entry: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	storageKey := path.Join(pluginType.String(), name) | ||||
| 	if version != "" { | ||||
| 		storageKey = path.Join(storageKey, version) | ||||
| 	storageKey := path.Join(plugin.Type.String(), plugin.Name) | ||||
| 	if plugin.Version != "" { | ||||
| 		storageKey = path.Join(storageKey, plugin.Version) | ||||
| 	} | ||||
| 	logicalEntry := logical.StorageEntry{ | ||||
| 		Key:   storageKey, | ||||
|   | ||||
| @@ -79,7 +79,15 @@ func TestPluginCatalog_CRUD(t *testing.T) { | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	command := filepath.Base(file.Name()) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginName, consts.PluginTypeDatabase, "", command, []string{"--test"}, []string{"FOO=BAR"}, []byte{'1'}) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    pluginName, | ||||
| 		Type:    consts.PluginTypeDatabase, | ||||
| 		Version: "", | ||||
| 		Command: command, | ||||
| 		Args:    []string{"--test"}, | ||||
| 		Env:     []string{"FOO=BAR"}, | ||||
| 		Sha256:  []byte{'1'}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @@ -163,7 +171,15 @@ func TestPluginCatalog_VersionedCRUD(t *testing.T) { | ||||
| 	const name = "mysql-database-plugin" | ||||
| 	const version = "1.0.0" | ||||
| 	command := fmt.Sprintf("%s", filepath.Base(file.Name())) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), name, consts.PluginTypeDatabase, version, command, []string{"--test"}, []string{"FOO=BAR"}, []byte{'1'}) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    name, | ||||
| 		Type:    consts.PluginTypeDatabase, | ||||
| 		Version: version, | ||||
| 		Command: command, | ||||
| 		Args:    []string{"--test"}, | ||||
| 		Env:     []string{"FOO=BAR"}, | ||||
| 		Sha256:  []byte{'1'}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @@ -270,13 +286,29 @@ func TestPluginCatalog_List(t *testing.T) { | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	command := filepath.Base(file.Name()) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", consts.PluginTypeDatabase, "", command, []string{"--test"}, []string{}, []byte{'1'}) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    "mysql-database-plugin", | ||||
| 		Type:    consts.PluginTypeDatabase, | ||||
| 		Version: "", | ||||
| 		Command: command, | ||||
| 		Args:    []string{"--test"}, | ||||
| 		Env:     []string{}, | ||||
| 		Sha256:  []byte{'1'}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// Set another plugin | ||||
| 	err = core.pluginCatalog.Set(context.Background(), "aaaaaaa", consts.PluginTypeDatabase, "", command, []string{"--test"}, []string{}, []byte{'1'}) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    "aaaaaaa", | ||||
| 		Type:    consts.PluginTypeDatabase, | ||||
| 		Version: "", | ||||
| 		Command: command, | ||||
| 		Args:    []string{"--test"}, | ||||
| 		Env:     []string{}, | ||||
| 		Sha256:  []byte{'1'}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @@ -341,31 +373,29 @@ func TestPluginCatalog_ListVersionedPlugins(t *testing.T) { | ||||
| 	defer file.Close() | ||||
|  | ||||
| 	command := filepath.Base(file.Name()) | ||||
| 	err = core.pluginCatalog.Set( | ||||
| 		context.Background(), | ||||
| 		"mysql-database-plugin", | ||||
| 		consts.PluginTypeDatabase, | ||||
| 		"", | ||||
| 		command, | ||||
| 		[]string{"--test"}, | ||||
| 		[]string{}, | ||||
| 		[]byte{'1'}, | ||||
| 	) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    "mysql-database-plugin", | ||||
| 		Type:    consts.PluginTypeDatabase, | ||||
| 		Version: "", | ||||
| 		Command: command, | ||||
| 		Args:    []string{"--test"}, | ||||
| 		Env:     []string{}, | ||||
| 		Sha256:  []byte{'1'}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// Set another plugin, with version information | ||||
| 	err = core.pluginCatalog.Set( | ||||
| 		context.Background(), | ||||
| 		"aaaaaaa", | ||||
| 		consts.PluginTypeDatabase, | ||||
| 		"1.1.0", | ||||
| 		command, | ||||
| 		[]string{"--test"}, | ||||
| 		[]string{}, | ||||
| 		[]byte{'1'}, | ||||
| 	) | ||||
| 	err = core.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    "aaaaaaa", | ||||
| 		Type:    consts.PluginTypeDatabase, | ||||
| 		Version: "1.1.0", | ||||
| 		Command: command, | ||||
| 		Args:    []string{"--test"}, | ||||
| 		Env:     []string{}, | ||||
| 		Sha256:  []byte{'1'}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @@ -458,7 +488,15 @@ func TestPluginCatalog_ListHandlesPluginNamesWithSlashes(t *testing.T) { | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, entry := range pluginsToRegister { | ||||
| 		err = core.pluginCatalog.Set(ctx, entry.Name, consts.PluginTypeCredential, entry.Version, command, nil, nil, nil) | ||||
| 		err = core.pluginCatalog.Set(ctx, pluginutil.SetPluginInput{ | ||||
| 			Name:    entry.Name, | ||||
| 			Type:    consts.PluginTypeCredential, | ||||
| 			Version: entry.Version, | ||||
| 			Command: command, | ||||
| 			Args:    nil, | ||||
| 			Env:     nil, | ||||
| 			Sha256:  nil, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|   | ||||
| @@ -593,7 +593,15 @@ func TestAddTestPlugin(t testing.T, c *Core, name string, pluginType consts.Plug | ||||
| 	c.pluginCatalog.directory = fullPath | ||||
|  | ||||
| 	args := []string{fmt.Sprintf("--test.run=%s", testFunc)} | ||||
| 	err = c.pluginCatalog.Set(context.Background(), name, pluginType, version, fileName, args, env, sum) | ||||
| 	err = c.pluginCatalog.Set(context.Background(), pluginutil.SetPluginInput{ | ||||
| 		Name:    name, | ||||
| 		Type:    pluginType, | ||||
| 		Version: version, | ||||
| 		Command: fileName, | ||||
| 		Args:    args, | ||||
| 		Env:     env, | ||||
| 		Sha256:  sum, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Tom Proctor
					Tom Proctor