From a67d58c1e874ce3a061cb83817721933bcd4563e Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Mon, 21 Aug 2023 09:32:11 -0700 Subject: [PATCH 01/20] add plugin runtime API --- api/plugin_runtime_types.go | 43 +++++++ api/sys_plugins_runtimes.go | 138 ++++++++++++++++++++ api/sys_plugins_runtimes_test.go | 92 ++++++++++++++ sdk/helper/consts/plugin_runtime_types.go | 44 +++++++ sdk/helper/pluginruntimeutil/runner.go | 19 +++ vault/core.go | 6 + vault/logical_system.go | 131 +++++++++++++++++++ vault/logical_system_paths.go | 120 ++++++++++++++++++ vault/plugin_runtime_catalog.go | 147 ++++++++++++++++++++++ vault/plugin_runtime_catalog_test.go | 84 +++++++++++++ 10 files changed, 824 insertions(+) create mode 100644 api/plugin_runtime_types.go create mode 100644 api/sys_plugins_runtimes.go create mode 100644 api/sys_plugins_runtimes_test.go create mode 100644 sdk/helper/consts/plugin_runtime_types.go create mode 100644 sdk/helper/pluginruntimeutil/runner.go create mode 100644 vault/plugin_runtime_catalog.go create mode 100644 vault/plugin_runtime_catalog_test.go diff --git a/api/plugin_runtime_types.go b/api/plugin_runtime_types.go new file mode 100644 index 000000000000..adb444b22c07 --- /dev/null +++ b/api/plugin_runtime_types.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package api + +// NOTE: this file was copied from +// https://github.com/hashicorp/vault/blob/main/sdk/helper/consts/plugin_runtime_types.go +// Any changes made should be made to both files at the same time. + +import "fmt" + +var PluginRuntimeTypes = []PluginRuntimeType{ + PluginRuntimeTypeUnknown, + PluginRuntimeTypeContainer, +} + +type PluginRuntimeType uint32 + +// This is a list of PluginRuntimeTypes used by Vault. +const ( + PluginRuntimeTypeUnknown PluginRuntimeType = iota + PluginRuntimeTypeContainer +) + +func (r PluginRuntimeType) String() string { + switch r { + case PluginRuntimeTypeContainer: + return "container" + case PluginRuntimeTypeUnknown: + fallthrough + default: + return "unsupported" + } +} + +func ParsePluginRuntimeType(PluginRuntimeType string) (PluginRuntimeType, error) { + switch PluginRuntimeType { + case "container": + return PluginRuntimeTypeContainer, nil + default: + return PluginRuntimeTypeUnknown, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType) + } +} diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go new file mode 100644 index 000000000000..8e91bda31e96 --- /dev/null +++ b/api/sys_plugins_runtimes.go @@ -0,0 +1,138 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package api + +import ( + "context" + "fmt" + "net/http" +) + +type PluginRuntimeDetails struct { + Type string `json:"type"` + Name string `json:"name"` + OCIRuntime string `json:"oci_runtime"` + ParentCGroup string `json:"parent_cgroup"` + CPU float32 `json:"cpu"` + Memory uint64 `json:"memory"` +} + +// GetPluginRuntimeInput is used as input to the GetPluginRuntime function. +type GetPluginRuntimeInput struct { + Name string `json:"-"` + + // Type of the plugin runtime. Required. + Type PluginRuntimeType `json:"type"` +} + +// GetPluginRuntimeResponse is the response from the GetPluginRuntime call. +type GetPluginRuntimeResponse struct { + Type string `json:"type"` + Name string `json:"name"` + OCIRuntime string `json:"oci_runtime"` + ParentCGroup string `json:"parent_cgroup"` + CPU float32 `json:"cpu"` + Memory uint64 `json:"memory"` +} + +// GetPluginRuntime wraps GetPluginRuntimeWithContext using context.Background. +func (c *Sys) GetPluginRuntime(i *GetPluginRuntimeInput) (*GetPluginRuntimeResponse, error) { + return c.GetPluginRuntimeWithContext(context.Background(), i) +} + +// GetPluginRuntimeWithContext retrieves information about the plugin. +func (c *Sys) GetPluginRuntimeWithContext(ctx context.Context, i *GetPluginRuntimeInput) (*GetPluginRuntimeResponse, error) { + ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) + defer cancelFunc() + + path := pluginRuntimeCatalogPathByType(i.Type, i.Name) + req := c.c.NewRequest(http.MethodGet, path) + + resp, err := c.c.rawRequestWithContext(ctx, req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result struct { + Data *GetPluginRuntimeResponse + } + err = resp.DecodeJSON(&result) + if err != nil { + return nil, err + } + return result.Data, err +} + +// RegisterPluginRuntimeInput is used as input to the RegisterPluginRuntime function. +type RegisterPluginRuntimeInput struct { + // Name is the name of the plugin. Required. + Name string `json:"-"` + + // Type of the plugin. Required. + Type PluginRuntimeType `json:"type"` + + OCIRuntime string `json:"oci_runtime,omitempty"` + ParentCGroup string `json:"parent_cgroup,omitempty"` + CPU float32 `json:"cpu,omitempty"` + Memory uint64 `json:"memory,omitempty"` +} + +// RegisterPluginRuntime wraps RegisterPluginWithContext using context.Background. +func (c *Sys) RegisterPluginRuntime(i *RegisterPluginRuntimeInput) error { + return c.RegisterPluginRuntimeWithContext(context.Background(), i) +} + +// RegisterPluginRuntimeWithContext registers the plugin with the given information. +func (c *Sys) RegisterPluginRuntimeWithContext(ctx context.Context, i *RegisterPluginRuntimeInput) error { + ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) + defer cancelFunc() + + path := pluginRuntimeCatalogPathByType(i.Type, i.Name) + req := c.c.NewRequest(http.MethodPut, path) + + if err := req.SetJSONBody(i); err != nil { + return err + } + + resp, err := c.c.rawRequestWithContext(ctx, req) + if err == nil { + defer resp.Body.Close() + } + return err +} + +// DeregisterPluginRuntimeInput is used as input to the DeregisterPluginRuntime function. +type DeregisterPluginRuntimeInput struct { + // Name is the name of the plugin runtime. Required. + Name string `json:"-"` + + // Type of the plugin. Required. + Type PluginRuntimeType `json:"type"` +} + +// DeregisterPluginRuntime wraps DeregisterPluginRuntimeWithContext using context.Background. +func (c *Sys) DeregisterPluginRuntime(i *DeregisterPluginRuntimeInput) error { + return c.DeregisterPluginRuntimeWithContext(context.Background(), i) +} + +// DeregisterPluginRuntimeWithContext removes the plugin with the given name from the plugin +// catalog. +func (c *Sys) DeregisterPluginRuntimeWithContext(ctx context.Context, i *DeregisterPluginRuntimeInput) error { + ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) + defer cancelFunc() + + path := pluginRuntimeCatalogPathByType(i.Type, i.Name) + req := c.c.NewRequest(http.MethodDelete, path) + resp, err := c.c.rawRequestWithContext(ctx, req) + if err == nil { + defer resp.Body.Close() + } + return err +} + +// pluginRuntimeCatalogPathByType is a helper to construct the proper API path by plugin type +func pluginRuntimeCatalogPathByType(runtimeType PluginRuntimeType, name string) string { + return fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", runtimeType, name) +} diff --git a/api/sys_plugins_runtimes_test.go b/api/sys_plugins_runtimes_test.go new file mode 100644 index 000000000000..4fddb046e041 --- /dev/null +++ b/api/sys_plugins_runtimes_test.go @@ -0,0 +1,92 @@ +package api + +import ( + "context" + "net/http" + "net/http/httptest" + "reflect" + "testing" +) + +func TestRegisterPluginRuntime(t *testing.T) { + mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandlerRegister)) + defer mockVaultServer.Close() + + cfg := DefaultConfig() + cfg.Address = mockVaultServer.URL + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + err = client.Sys().RegisterPluginRuntimeWithContext(context.Background(), &RegisterPluginRuntimeInput{ + Name: "gvisor", + Type: PluginRuntimeTypeContainer, + OCIRuntime: "runsc", + ParentCGroup: "/cpulimit-cgroup/", + CPU: 1, + Memory: 10000, + }) + if err != nil { + t.Fatal(err) + } +} + +func TestGetPluginRuntime(t *testing.T) { + for name, tc := range map[string]struct { + body string + expected GetPluginRuntimeResponse + }{ + "gvisor": { + body: getPluginRuntimeResponse, + expected: GetPluginRuntimeResponse{ + Name: "gvisor", + Type: PluginRuntimeTypeContainer.String(), + OCIRuntime: "runsc", + ParentCGroup: "/cpulimit-cgroup/", + CPU: 1, + Memory: 10000, + }, + }, + } { + t.Run(name, func(t *testing.T) { + mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandlerInfo(tc.body))) + defer mockVaultServer.Close() + + cfg := DefaultConfig() + cfg.Address = mockVaultServer.URL + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + input := GetPluginRuntimeInput{ + Name: "gvisor", + Type: PluginRuntimeTypeContainer, + } + + info, err := client.Sys().GetPluginRuntimeWithContext(context.Background(), &input) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(tc.expected, *info) { + t.Errorf("expected: %#v\ngot: %#v", tc.expected, info) + } + }) + } +} + +const getPluginRuntimeResponse = `{ + "request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456", + "data": { + "name": "gvisor", + "type": "container", + "oci_runtime": "runsc", + "parent_cgroup": "/cpulimit-cgroup/", + "cpu": 1, + "memory": 10000 + }, + "warnings": null, + "auth": null +}` diff --git a/sdk/helper/consts/plugin_runtime_types.go b/sdk/helper/consts/plugin_runtime_types.go new file mode 100644 index 000000000000..8b81f4843c53 --- /dev/null +++ b/sdk/helper/consts/plugin_runtime_types.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package consts + +// TODO thy +// NOTE: this file has been copied to +// https://github.com/hashicorp/vault/blob/main/api/plugin_runtime_types.go +// Any changes made should be made to both files at the same time. + +import "fmt" + +var PluginRuntimeTypes = []PluginRuntimeType{ + PluginRuntimeTypeUnknown, + PluginRuntimeTypeContainer, +} + +type PluginRuntimeType uint32 + +// This is a list of PluginRuntimeTypes used by Vault. +const ( + PluginRuntimeTypeUnknown PluginRuntimeType = iota + PluginRuntimeTypeContainer +) + +func (r PluginRuntimeType) String() string { + switch r { + case PluginRuntimeTypeContainer: + return "container" + case PluginRuntimeTypeUnknown: + fallthrough + default: + return "unsupported" + } +} + +func ParsePluginRuntimeType(PluginRuntimeType string) (PluginRuntimeType, error) { + switch PluginRuntimeType { + case "container": + return PluginRuntimeTypeContainer, nil + default: + return PluginRuntimeTypeUnknown, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType) + } +} diff --git a/sdk/helper/pluginruntimeutil/runner.go b/sdk/helper/pluginruntimeutil/runner.go new file mode 100644 index 000000000000..0a660abefb3d --- /dev/null +++ b/sdk/helper/pluginruntimeutil/runner.go @@ -0,0 +1,19 @@ +package pluginruntimeutil + +import "github.com/hashicorp/vault/sdk/helper/consts" + +const ( + DefaultOCIRuntime = "runsc" + DefaultCPU = 0.5 + DefaultMemory = 10000 +) + +// PluginRuntimeRunner defines the metadata needed to run a plugin runtime +type PluginRuntimeRunner struct { + Name string `json:"name" structs:"name"` + Type consts.PluginRuntimeType `json:"type" structs:"type"` + OCIRuntime string `json:"oci_runtime" structs:"oci_runtime"` + ParentCGroup string `json:"parent_cgroup" structs:"parent_cgroup"` + CPU float32 `json:"cpu" structs:"cpu"` + Memory uint64 `json:"memory" structs:"memory"` +} diff --git a/vault/core.go b/vault/core.go index dc76def7f883..3b35788d59d7 100644 --- a/vault/core.go +++ b/vault/core.go @@ -548,6 +548,9 @@ type Core struct { // pluginCatalog is used to manage plugin configurations pluginCatalog *PluginCatalog + // pluginRuntimeCatalog is used to manage plugin runtime configurations + pluginRuntimeCatalog *PluginRuntimeCatalog + // The userFailedLoginInfo map has user failed login information. // It has user information (alias-name and mount accessor) as a key // and login counter, last failed login time as value @@ -2288,6 +2291,9 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c return err } } + if err := c.setupPluginRuntimeCatalog(ctx); err != nil { + return err + } if err := c.setupPluginCatalog(ctx); err != nil { return err } diff --git a/vault/logical_system.go b/vault/logical_system.go index f81ed292bc6c..388b1c8bcfb5 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -43,6 +43,7 @@ import ( "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/helper/pluginruntimeutil" "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/helper/roottoken" "github.com/hashicorp/vault/sdk/helper/wrapping" @@ -113,6 +114,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { "config/auditing/*", "config/ui/headers/*", "plugins/catalog/*", + "plugins/runtimes/catalog/*", "revoke-prefix/*", "revoke-force/*", "leases/revoke-prefix/*", @@ -186,6 +188,9 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogListPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogCRUDPath()) b.Backend.Paths = append(b.Backend.Paths, b.pluginsReloadPath()) + b.Backend.Paths = append(b.Backend.Paths, b.pluginsRuntimesCatalogCRUDPath()) + // TODO thy + // b.Backend.Paths = append(b.Backend.Paths, b.pluginsRuntimesCatalogListPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.auditPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.mountPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.authPaths()...) @@ -724,6 +729,104 @@ func (b *SystemBackend) handlePluginReloadUpdate(ctx context.Context, req *logic return &r, nil } +func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ *logical.Request, d *framework.FieldData) (*logical.Response, error) { + runtimeName := d.Get("name").(string) + if runtimeName == "" { + return logical.ErrorResponse("missing plugin runtime name"), nil + } + + // TODO validate name + + runtimeTypeStr := d.Get("type").(string) + if runtimeTypeStr == "" { + return logical.ErrorResponse("missing plugin runtime type"), nil + } + + runtimeType, err := consts.ParsePluginRuntimeType(runtimeTypeStr) + if err != nil { + return logical.ErrorResponse(err.Error()), nil + } + + switch runtimeType { + case consts.PluginRuntimeTypeContainer: + ociRuntime := d.Get("oci_runtime").(string) + if ociRuntime == "" { + ociRuntime = pluginruntimeutil.DefaultOCIRuntime + } + parentCGroup := d.Get("parent_cgroup").(string) + cpu := d.Get("cpu").(float32) + if cpu == 0 { + cpu = pluginruntimeutil.DefaultCPU + } + memory := d.Get("memory").(uint64) + if memory == 0 { + memory = pluginruntimeutil.DefaultMemory + } + err := b.Core.pluginRuntimeCatalog.Set(ctx, runtimeName, runtimeType, ociRuntime, parentCGroup, cpu, memory) + if err != nil { + return logical.ErrorResponse(err.Error()), nil + } + } + return nil, nil +} + +func (b *SystemBackend) handlePluginRuntimeCatalogDelete(ctx context.Context, _ *logical.Request, d *framework.FieldData) (*logical.Response, error) { + runtimeName := d.Get("name").(string) + if runtimeName == "" { + return logical.ErrorResponse("missing plugin runtime name"), nil + } + + // TODO validate name + + runtimeTypeStr := d.Get("type").(string) + if runtimeTypeStr == "" { + return logical.ErrorResponse("missing plugin runtime type"), nil + } + + runtimeType, err := consts.ParsePluginRuntimeType(runtimeTypeStr) + if err != nil { + return logical.ErrorResponse(err.Error()), nil + } + + err = b.Core.pluginRuntimeCatalog.Delete(ctx, runtimeName, runtimeType) + if err != nil { + return logical.ErrorResponse(err.Error()), nil + } + return nil, nil +} + +func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *logical.Request, d *framework.FieldData) (*logical.Response, error) { + runtimeName := d.Get("name").(string) + if runtimeName == "" { + return logical.ErrorResponse("missing plugin runtime name"), nil + } + + // TODO validate name + + runtimeTypeStr := d.Get("type").(string) + if runtimeTypeStr == "" { + return logical.ErrorResponse("missing plugin runtime type"), nil + } + + runtimeType, err := consts.ParsePluginRuntimeType(runtimeTypeStr) + if err != nil { + return logical.ErrorResponse(err.Error()), nil + } + + runner, err := b.Core.pluginRuntimeCatalog.Get(ctx, runtimeName, runtimeType) + if err != nil { + return logical.ErrorResponse(err.Error()), nil + } + return &logical.Response{Data: map[string]interface{}{ + "name": runner.Name, + "type": runner.Type, + "oci_runtime": runner.OCIRuntime, + "parent_cgroup": runner.ParentCGroup, + "cpu": runner.CPU, + "memory": runner.Memory, + }}, nil +} + // handleAuditedHeaderUpdate creates or overwrites a header entry func (b *SystemBackend) handleAuditedHeaderUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { header := d.Get("header").(string) @@ -5896,6 +5999,34 @@ Each entry is of the form "key=value".`, "The semantic version of the plugin to use.", "", }, + "plugin-runtime-catalog": { + "", // TODO thy def. + "", // TODO thy def. + }, + "plugin-runtime-catalog_name": { + "", // TODO thy def. + "", + }, + "plugin-runtime-catalog_type": { + "", // TODO thy def. + "", + }, + "plugin-runtime-catalog_oci-runtime": { + "", // TODO thy def. + "", + }, + "plugin-runtime-catalog_parent-cgroup": { + "", // TODO thy def. + "", + }, + "plugin-runtime-catalog_cpu": { + "", // TODO thy def. + "", + }, + "plugin-runtime-catalog_memory": { + "", // TODO thy def. + "", + }, "leases": { `View or list lease metadata.`, ` diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index c71e5e85983f..2b8c887916ca 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -2093,6 +2093,126 @@ func (b *SystemBackend) pluginsReloadPath() *framework.Path { } } +func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { + return &framework.Path{ + Pattern: "plugins/runtimes/catalog(/(?P<type>container))?/(?P<name>.+)", + + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: "plugins-runtimes-catalog", + }, + + Fields: map[string]*framework.FieldSchema{ + "name": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_name"][0]), + }, + "type": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_type"][0]), + }, + "oci_runtime": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_oci-runtime"][0]), + }, + "parent_cgroup": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_parent-cgroup"][0]), + }, + "cpu": { + Type: framework.TypeFloat, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), + }, + "memory": { + Type: framework.TypeInt64, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory"][0]), + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.UpdateOperation: &framework.PathOperation{ + Callback: b.handlePluginRuntimeCatalogUpdate, + DisplayAttrs: &framework.DisplayAttributes{ + OperationVerb: "register", + OperationSuffix: "plugin-runtime|plugin-runtime-with-type|plugin-runtime-with-type-and-name", // TODO + }, + Responses: map[int][]framework.Response{ + http.StatusOK: {{ + Description: "OK", + }}, + }, + Summary: "Register a new plugin runtime, or updates an existing one with the supplied name.", + }, + logical.DeleteOperation: &framework.PathOperation{ + Callback: b.handlePluginRuntimeCatalogDelete, + DisplayAttrs: &framework.DisplayAttributes{ + OperationVerb: "remove", + OperationSuffix: "plugin-runtime|plugin-runtime-with-type|plugin-runtime-with-type-and-name", // TODO + }, + Responses: map[int][]framework.Response{ + http.StatusOK: {{ + Description: "OK", + Fields: map[string]*framework.FieldSchema{}, + }}, + }, + Summary: "Remove the plugin runtime with the given name.", + }, + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handlePluginRuntimeCatalogRead, + DisplayAttrs: &framework.DisplayAttributes{ + OperationVerb: "read", + OperationSuffix: "plugin-runtime-configuration|plugin-runtime-configuration-with-type|plugin-runtime-configuration-with-type-and-name", + }, + Responses: map[int][]framework.Response{ + http.StatusOK: {{ + Description: "OK", + Fields: map[string]*framework.FieldSchema{ + "name": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_name"][0]), + Required: true, + }, + "type": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_type"][0]), + Required: true, + }, + "oci_runtime": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_oci-runtime"][0]), + Required: true, + }, + "parent_cgroup": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_parent-cgroup"][0]), + Required: true, + }, + "cpu": { + Type: framework.TypeFloat, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), + Required: true, + }, + "memory": { + Type: framework.TypeInt64, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory"][0]), + Required: true, + }, + }, + }}, + }, + Summary: "Return the configuration data for the plugin runtime with the given name.", + }, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["plugin-runtime-catalog"][0]), + HelpDescription: strings.TrimSpace(sysHelp["plugin-runtime-catalog"][1]), + } +} + +// TODO thy +//func (b *SystemBackend) pluginsRuntimesCatalogListPaths() []*framework.Path { +// return []*framework.Path{} +//} + func (b *SystemBackend) toolsPaths() []*framework.Path { return []*framework.Path{ { diff --git a/vault/plugin_runtime_catalog.go b/vault/plugin_runtime_catalog.go new file mode 100644 index 000000000000..d11ca2afa2e1 --- /dev/null +++ b/vault/plugin_runtime_catalog.go @@ -0,0 +1,147 @@ +package vault + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "path" + "sync" + + log "github.com/hashicorp/go-hclog" + + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/hashicorp/vault/sdk/helper/jsonutil" + "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" + "github.com/hashicorp/vault/sdk/logical" +) + +var ( + pluginRuntimeCatalogPath = "core/plugin-runtime-catalog/" + ErrPluginRuntimeNotFound = errors.New("plugin runtime not found") + ErrPluginRuntimeBadType = errors.New("unable to determine plugin runtime type") + ErrPluginRuntimeBadContainerConfig = errors.New("bad container config") +) + +// PluginRuntimeCatalog keeps a record of plugin runtimes. Plugin runtimes need +// to be registered to the catalog before they can be used in backends when registering plugins with runtimes +type PluginRuntimeCatalog struct { + catalogView *BarrierView + logger log.Logger + + lock sync.RWMutex +} + +func (c *Core) setupPluginRuntimeCatalog(ctx context.Context) error { + c.pluginRuntimeCatalog = &PluginRuntimeCatalog{ + catalogView: NewBarrierView(c.barrier, pluginRuntimeCatalogPath), + logger: c.logger, + } + + if c.logger.IsInfo() { + c.logger.Info("successfully setup plugin runtime catalog") + } + + return nil +} + +// Get retrieves a plugin runtime with the specified name from the catalog +// It returns a PluginRuntimeRunner or an error if no plugin runtime was found. +func (c *PluginRuntimeCatalog) Get(ctx context.Context, name string, prt consts.PluginRuntimeType) (*pluginruntimeutil.PluginRuntimeRunner, error) { + storageKey := path.Join(prt.String(), name) + c.lock.RLock() + defer c.lock.RUnlock() + entry, err := c.catalogView.Get(ctx, storageKey) + if err != nil { + return nil, fmt.Errorf("failed to retrieve plugin runtime %q %q: %w", prt.String(), name, err) + } + if entry == nil { + return nil, fmt.Errorf("failed to retrieve plugin %q %q: %w", prt.String(), name, err) + } + runner := new(pluginruntimeutil.PluginRuntimeRunner) + if err := jsonutil.DecodeJSON(entry.Value, runner); err != nil { + return nil, fmt.Errorf("failed to decode plugin runtime entry: %w", err) + } + if runner.Type != prt { + return nil, nil + } + return runner, nil +} + +// Set registers a new plugin with the catalog, or updates an existing plugin runtime +func (c *PluginRuntimeCatalog) Set(ctx context.Context, name string, prt consts.PluginRuntimeType, ociRuntime string, parentCGroup string, cpu float32, memory uint64) error { + c.lock.Lock() + defer c.lock.Unlock() + + entry := &pluginruntimeutil.PluginRuntimeRunner{ + Name: name, + Type: prt, + OCIRuntime: ociRuntime, + ParentCGroup: parentCGroup, + CPU: cpu, + Memory: memory, + } + + // TODO what happens to the running existing container with(out) running plugins. Need to shut it down before setting it? + buf, err := json.Marshal(entry) + if err != nil { + return fmt.Errorf("failed to encode plugin entry: %w", err) + } + + storageKey := path.Join(prt.String(), name) + logicalEntry := logical.StorageEntry{ + Key: storageKey, + Value: buf, + } + + if err := c.catalogView.Put(ctx, &logicalEntry); err != nil { + return fmt.Errorf("failed to persist plugin runtime entry: %w", err) + } + return err +} + +// Delete is used to remove an external plugin from the catalog. Builtin plugins +// can not be deleted. +func (c *PluginRuntimeCatalog) Delete(ctx context.Context, name string, prt consts.PluginRuntimeType) error { + c.lock.Lock() + defer c.lock.Unlock() + + storageKey := path.Join(prt.String(), name) + out, err := c.catalogView.Get(ctx, storageKey) + if err != nil || out == nil { + return ErrPluginRuntimeNotFound + } + + // TODO what happens to the running existing container with(out) running plugins. Need to shut it down before setting it? + return c.catalogView.Delete(ctx, storageKey) +} + +func (c *PluginRuntimeCatalog) List(ctx context.Context, prt consts.PluginRuntimeType) ([]string, error) { + c.lock.RLock() + defer c.lock.RUnlock() + + var retList []string + keys, err := logical.CollectKeys(ctx, c.catalogView) + if err != nil { + return nil, err + } + + for _, key := range keys { + entry, err := c.catalogView.Get(ctx, key) + if err != nil || entry == nil { + continue + } + + runner := new(pluginruntimeutil.PluginRuntimeRunner) + if err := jsonutil.DecodeJSON(entry.Value, runner); err != nil { + return nil, fmt.Errorf("failed to decode plugin runtime entry: %w", err) + } + + if runner.Type != prt { + continue + } + + retList = append(retList, runner.Name) + } + return retList, nil +} diff --git a/vault/plugin_runtime_catalog_test.go b/vault/plugin_runtime_catalog_test.go new file mode 100644 index 000000000000..0fada7832d3c --- /dev/null +++ b/vault/plugin_runtime_catalog_test.go @@ -0,0 +1,84 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package vault + +import ( + "context" + "reflect" + "testing" + + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" +) + +func TestPluginRuntimeCatalog_CRUD(t *testing.T) { + core, _, _ := TestCoreUnsealed(t) + ctx := context.Background() + + expected := &pluginruntimeutil.PluginRuntimeRunner{ + Name: "gvisor", + Type: consts.PluginRuntimeTypeContainer, + OCIRuntime: "runsc", + ParentCGroup: "/cpulimit-cgroup/", + CPU: 1, + Memory: 10000, + } + + // Set new plugin runtime + err := core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.ParentCGroup, expected.CPU, expected.Memory) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Get plugin runtime + runner, err := core.pluginRuntimeCatalog.Get(ctx, expected.Name, expected.Type) + if err != nil { + t.Fatalf("err: %v", err) + } + if !reflect.DeepEqual(expected, runner) { + t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", runner, expected) + } + + // Set existing plugin runtime + expected.ParentCGroup = "memorylimit-cgroup" + expected.CPU = 0.5 + expected.Memory = 5000 + err = core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.ParentCGroup, expected.CPU, expected.Memory) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Get plugin runtime again + runner, err = core.pluginRuntimeCatalog.Get(ctx, expected.Name, expected.Type) + if err != nil { + t.Fatalf("err: %v", err) + } + + if !reflect.DeepEqual(expected, runner) { + t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", runner, expected) + } + + names, err := core.pluginRuntimeCatalog.List(ctx, expected.Type) + if err != nil { + t.Fatalf("err: %v", err) + } + if len(names) != 1 { + t.Fatalf("expected plugin runtime catalog to have 1 container runtime but got %d", len(names)) + } + + // Delete plugin runtime + err = core.pluginRuntimeCatalog.Delete(ctx, expected.Name, expected.Type) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Assert the plugin runtime catalog is empty + names, err = core.pluginRuntimeCatalog.List(ctx, expected.Type) + if err != nil { + t.Fatalf("err: %v", err) + } + if len(names) != 0 { + t.Fatalf("expected plugin runtime catalog to have 0 container runtimes but got %d", len(names)) + } +} From 2cfb2a685042b863449fcccb6b781d998af7b6e2 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Tue, 22 Aug 2023 18:32:06 -0700 Subject: [PATCH 02/20] Update sdk/helper/pluginruntimeutil/runner.go Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com> --- sdk/helper/pluginruntimeutil/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/helper/pluginruntimeutil/runner.go b/sdk/helper/pluginruntimeutil/runner.go index 0a660abefb3d..40bbe6963552 100644 --- a/sdk/helper/pluginruntimeutil/runner.go +++ b/sdk/helper/pluginruntimeutil/runner.go @@ -9,7 +9,7 @@ const ( ) // PluginRuntimeRunner defines the metadata needed to run a plugin runtime -type PluginRuntimeRunner struct { +type PluginRuntimeConfig struct { Name string `json:"name" structs:"name"` Type consts.PluginRuntimeType `json:"type" structs:"type"` OCIRuntime string `json:"oci_runtime" structs:"oci_runtime"` From 9c360b679efd818f6e2693a4099b1395631e1b48 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Tue, 22 Aug 2023 18:32:06 -0700 Subject: [PATCH 03/20] Update sdk/helper/pluginruntimeutil/runner.go Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com> --- api/plugin_runtime_types.go | 8 +- api/sys_plugins_runtimes.go | 90 ++++++++++++++++- api/sys_plugins_runtimes_test.go | 115 +++++++++++++++++++++- sdk/helper/consts/plugin_runtime_types.go | 9 +- sdk/helper/pluginruntimeutil/runner.go | 10 +- vault/logical_system.go | 108 ++++++++++++++------ vault/logical_system_paths.go | 87 ++++++++++++++-- vault/plugin_runtime_catalog.go | 12 +-- vault/plugin_runtime_catalog_test.go | 12 +-- 9 files changed, 378 insertions(+), 73 deletions(-) diff --git a/api/plugin_runtime_types.go b/api/plugin_runtime_types.go index adb444b22c07..d3acd0d002c4 100644 --- a/api/plugin_runtime_types.go +++ b/api/plugin_runtime_types.go @@ -10,7 +10,7 @@ package api import "fmt" var PluginRuntimeTypes = []PluginRuntimeType{ - PluginRuntimeTypeUnknown, + PluginRuntimeTypeUnsupported, PluginRuntimeTypeContainer, } @@ -18,7 +18,7 @@ type PluginRuntimeType uint32 // This is a list of PluginRuntimeTypes used by Vault. const ( - PluginRuntimeTypeUnknown PluginRuntimeType = iota + PluginRuntimeTypeUnsupported PluginRuntimeType = iota PluginRuntimeTypeContainer ) @@ -26,8 +26,6 @@ func (r PluginRuntimeType) String() string { switch r { case PluginRuntimeTypeContainer: return "container" - case PluginRuntimeTypeUnknown: - fallthrough default: return "unsupported" } @@ -38,6 +36,6 @@ func ParsePluginRuntimeType(PluginRuntimeType string) (PluginRuntimeType, error) case "container": return PluginRuntimeTypeContainer, nil default: - return PluginRuntimeTypeUnknown, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType) + return PluginRuntimeTypeUnsupported, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType) } } diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index 8e91bda31e96..c68facd22e75 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -5,19 +5,34 @@ package api import ( "context" + "errors" "fmt" "net/http" + + "github.com/mitchellh/mapstructure" ) type PluginRuntimeDetails struct { Type string `json:"type"` Name string `json:"name"` OCIRuntime string `json:"oci_runtime"` - ParentCGroup string `json:"parent_cgroup"` + CgroupParent string `json:"cgroup_parent"` CPU float32 `json:"cpu"` Memory uint64 `json:"memory"` } +// ListPluginRuntimesInput is used as input to the ListPluginRuntimes function. +type ListPluginRuntimesInput struct { + // Type of the plugin. Required. + Type PluginRuntimeType `json:"type"` +} + +// ListPluginRuntimesResponse is the response from the ListPluginRuntimes call. +type ListPluginRuntimesResponse struct { + // RuntimesByType is the list of plugin runtimes by type. + RuntimesByType map[PluginRuntimeType][]string `json:"types"` +} + // GetPluginRuntimeInput is used as input to the GetPluginRuntime function. type GetPluginRuntimeInput struct { Name string `json:"-"` @@ -31,7 +46,7 @@ type GetPluginRuntimeResponse struct { Type string `json:"type"` Name string `json:"name"` OCIRuntime string `json:"oci_runtime"` - ParentCGroup string `json:"parent_cgroup"` + CgroupParent string `json:"cgroup_parent"` CPU float32 `json:"cpu"` Memory uint64 `json:"memory"` } @@ -74,7 +89,7 @@ type RegisterPluginRuntimeInput struct { Type PluginRuntimeType `json:"type"` OCIRuntime string `json:"oci_runtime,omitempty"` - ParentCGroup string `json:"parent_cgroup,omitempty"` + CgroupParent string `json:"cgroup_parent,omitempty"` CPU float32 `json:"cpu,omitempty"` Memory uint64 `json:"memory,omitempty"` } @@ -132,6 +147,75 @@ func (c *Sys) DeregisterPluginRuntimeWithContext(ctx context.Context, i *Deregis return err } +// ListPluginRuntimes wraps ListPluginRuntimesWithContext using context.Background. +func (c *Sys) ListPluginRuntimes(i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { + return c.ListPluginRuntimesWithContext(context.Background(), i) +} + +// ListPluginRuntimesWithContext lists all plugin runtimes in the catalog and returns their names as a +// list of strings. +func (c *Sys) ListPluginRuntimesWithContext(ctx context.Context, i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { + ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) + defer cancelFunc() + + if i != nil && i.Type == PluginRuntimeTypeUnsupported { + return nil, fmt.Errorf("%q is not a supported runtime type", i.Type.String()) + } + + resp, err := c.c.rawRequestWithContext(ctx, c.c.NewRequest(http.MethodGet, "/v1/sys/plugins/runtimes/catalog")) + if err != nil && resp == nil { + return nil, err + } + if resp == nil { + return nil, nil + } + defer resp.Body.Close() + + secret, err := ParseSecret(resp.Body) + if err != nil { + return nil, err + } + if secret == nil || secret.Data == nil { + return nil, errors.New("data from server response is empty") + } + + result := &ListPluginRuntimesResponse{ + RuntimesByType: make(map[PluginRuntimeType][]string), + } + + // return all runtimes in the catalog + if i == nil { + for _, runtimeType := range PluginRuntimeTypes { + runtimesRaw, ok := secret.Data[runtimeType.String()] + if !ok { + continue + } + + var tmp []string + if err = mapstructure.Decode(runtimesRaw, &tmp); err != nil { + return nil, err + } + result.RuntimesByType[runtimeType] = tmp + } + return result, nil + } + + switch i.Type { + default: + runtimesRaw, ok := secret.Data[i.Type.String()] + if !ok { + return nil, fmt.Errorf("no %s entry in returned data", i.Type.String()) + } + + var tmp []string + if err := mapstructure.Decode(runtimesRaw, &tmp); err != nil { + return nil, err + } + result.RuntimesByType[i.Type] = tmp + } + return result, nil +} + // pluginRuntimeCatalogPathByType is a helper to construct the proper API path by plugin type func pluginRuntimeCatalogPathByType(runtimeType PluginRuntimeType, name string) string { return fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", runtimeType, name) diff --git a/api/sys_plugins_runtimes_test.go b/api/sys_plugins_runtimes_test.go index 4fddb046e041..d3d06af0da09 100644 --- a/api/sys_plugins_runtimes_test.go +++ b/api/sys_plugins_runtimes_test.go @@ -23,7 +23,7 @@ func TestRegisterPluginRuntime(t *testing.T) { Name: "gvisor", Type: PluginRuntimeTypeContainer, OCIRuntime: "runsc", - ParentCGroup: "/cpulimit-cgroup/", + CgroupParent: "/cpulimit/", CPU: 1, Memory: 10000, }) @@ -43,7 +43,7 @@ func TestGetPluginRuntime(t *testing.T) { Name: "gvisor", Type: PluginRuntimeTypeContainer.String(), OCIRuntime: "runsc", - ParentCGroup: "/cpulimit-cgroup/", + CgroupParent: "/cpulimit/", CPU: 1, Memory: 10000, }, @@ -77,16 +77,125 @@ func TestGetPluginRuntime(t *testing.T) { } } +func TestListPluginRuntimeTyped(t *testing.T) { + for _, tc := range []struct { + runtimeType PluginRuntimeType + body string + expectedResponse *ListPluginRuntimesResponse + expectedErrNil bool + }{ + { + runtimeType: PluginRuntimeTypeContainer, + body: listPluginRuntimeTypedResponse, + expectedResponse: &ListPluginRuntimesResponse{ + RuntimesByType: map[PluginRuntimeType][]string{ + PluginRuntimeTypeContainer: {"gvisor"}, + }, + }, + expectedErrNil: true, + }, + { + runtimeType: PluginRuntimeTypeUnsupported, + body: listPluginRuntimeTypedResponse, + expectedResponse: nil, + expectedErrNil: false, + }, + } { + t.Run(tc.runtimeType.String(), func(t *testing.T) { + mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandlerInfo(tc.body))) + defer mockVaultServer.Close() + + cfg := DefaultConfig() + cfg.Address = mockVaultServer.URL + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + input := ListPluginRuntimesInput{ + Type: tc.runtimeType, + } + + list, err := client.Sys().ListPluginRuntimesWithContext(context.Background(), &input) + if tc.expectedErrNil && err != nil { + t.Fatal(err) + } + + if (tc.expectedErrNil && !reflect.DeepEqual(tc.expectedResponse, list)) || (!tc.expectedErrNil && list != nil) { + t.Errorf("expected: %#v\ngot: %#v", tc.expectedResponse, list) + } + }) + } +} + +func TestListPluginRuntimeUntyped(t *testing.T) { + for _, tc := range []struct { + body string + expectedResponse *ListPluginRuntimesResponse + expectedErrNil bool + }{ + { + body: listPluginRuntimeUntypedResponse, + expectedResponse: &ListPluginRuntimesResponse{ + RuntimesByType: map[PluginRuntimeType][]string{ + PluginRuntimeTypeContainer: {"gvisor", "foo", "bar"}, + }, + }, + expectedErrNil: true, + }, + } { + t.Run("", func(t *testing.T) { + mockVaultServer := httptest.NewServer(http.HandlerFunc(mockVaultHandlerInfo(tc.body))) + defer mockVaultServer.Close() + + cfg := DefaultConfig() + cfg.Address = mockVaultServer.URL + client, err := NewClient(cfg) + if err != nil { + t.Fatal(err) + } + + info, err := client.Sys().ListPluginRuntimesWithContext(context.Background(), nil) + if tc.expectedErrNil && err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(tc.expectedResponse, info) { + t.Errorf("expected: %#v\ngot: %#v", tc.expectedResponse, info) + } + }) + } +} + const getPluginRuntimeResponse = `{ "request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456", "data": { "name": "gvisor", "type": "container", "oci_runtime": "runsc", - "parent_cgroup": "/cpulimit-cgroup/", + "cgroup_parent": "/cpulimit/", "cpu": 1, "memory": 10000 }, "warnings": null, "auth": null }` + +const listPluginRuntimeTypedResponse = `{ + "request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456", + "data": { + "container": ["gvisor"] + }, + "warnings": null, + "auth": null +} +` + +const listPluginRuntimeUntypedResponse = `{ + "request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456", + "data": { + "container": ["gvisor", "foo", "bar"] + }, + "warnings": null, + "auth": null +}` diff --git a/sdk/helper/consts/plugin_runtime_types.go b/sdk/helper/consts/plugin_runtime_types.go index 8b81f4843c53..63b8127ec2c9 100644 --- a/sdk/helper/consts/plugin_runtime_types.go +++ b/sdk/helper/consts/plugin_runtime_types.go @@ -3,7 +3,6 @@ package consts -// TODO thy // NOTE: this file has been copied to // https://github.com/hashicorp/vault/blob/main/api/plugin_runtime_types.go // Any changes made should be made to both files at the same time. @@ -11,7 +10,7 @@ package consts import "fmt" var PluginRuntimeTypes = []PluginRuntimeType{ - PluginRuntimeTypeUnknown, + PluginRuntimeTypeUnsupported, PluginRuntimeTypeContainer, } @@ -19,7 +18,7 @@ type PluginRuntimeType uint32 // This is a list of PluginRuntimeTypes used by Vault. const ( - PluginRuntimeTypeUnknown PluginRuntimeType = iota + PluginRuntimeTypeUnsupported PluginRuntimeType = iota PluginRuntimeTypeContainer ) @@ -27,8 +26,6 @@ func (r PluginRuntimeType) String() string { switch r { case PluginRuntimeTypeContainer: return "container" - case PluginRuntimeTypeUnknown: - fallthrough default: return "unsupported" } @@ -39,6 +36,6 @@ func ParsePluginRuntimeType(PluginRuntimeType string) (PluginRuntimeType, error) case "container": return PluginRuntimeTypeContainer, nil default: - return PluginRuntimeTypeUnknown, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType) + return PluginRuntimeTypeUnsupported, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType) } } diff --git a/sdk/helper/pluginruntimeutil/runner.go b/sdk/helper/pluginruntimeutil/runner.go index 0a660abefb3d..1deaa6d195ba 100644 --- a/sdk/helper/pluginruntimeutil/runner.go +++ b/sdk/helper/pluginruntimeutil/runner.go @@ -4,16 +4,16 @@ import "github.com/hashicorp/vault/sdk/helper/consts" const ( DefaultOCIRuntime = "runsc" - DefaultCPU = 0.5 - DefaultMemory = 10000 + DefaultCPU = 0.1 + DefaultMemory = 100000000 ) -// PluginRuntimeRunner defines the metadata needed to run a plugin runtime -type PluginRuntimeRunner struct { +// PluginRuntimeConfig defines the metadata needed to run a plugin runtime +type PluginRuntimeConfig struct { Name string `json:"name" structs:"name"` Type consts.PluginRuntimeType `json:"type" structs:"type"` OCIRuntime string `json:"oci_runtime" structs:"oci_runtime"` - ParentCGroup string `json:"parent_cgroup" structs:"parent_cgroup"` + CgroupParent string `json:"cgroup_parent" structs:"cgroup_parent"` CPU float32 `json:"cpu" structs:"cpu"` Memory uint64 `json:"memory" structs:"memory"` } diff --git a/vault/logical_system.go b/vault/logical_system.go index 388b1c8bcfb5..27d192c082d9 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -189,8 +189,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogCRUDPath()) b.Backend.Paths = append(b.Backend.Paths, b.pluginsReloadPath()) b.Backend.Paths = append(b.Backend.Paths, b.pluginsRuntimesCatalogCRUDPath()) - // TODO thy - // b.Backend.Paths = append(b.Backend.Paths, b.pluginsRuntimesCatalogListPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.pluginsRuntimesCatalogListPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.auditPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.mountPaths()...) b.Backend.Paths = append(b.Backend.Paths, b.authPaths()...) @@ -735,8 +734,6 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ return logical.ErrorResponse("missing plugin runtime name"), nil } - // TODO validate name - runtimeTypeStr := d.Get("type").(string) if runtimeTypeStr == "" { return logical.ErrorResponse("missing plugin runtime type"), nil @@ -753,19 +750,21 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ if ociRuntime == "" { ociRuntime = pluginruntimeutil.DefaultOCIRuntime } - parentCGroup := d.Get("parent_cgroup").(string) - cpu := d.Get("cpu").(float32) + cgroupParent := d.Get("cgroup_parent").(string) + cpu := float32(d.Get("cpu").(float64)) if cpu == 0 { cpu = pluginruntimeutil.DefaultCPU } - memory := d.Get("memory").(uint64) + memory := uint64(d.Get("memory").(int64)) if memory == 0 { memory = pluginruntimeutil.DefaultMemory } - err := b.Core.pluginRuntimeCatalog.Set(ctx, runtimeName, runtimeType, ociRuntime, parentCGroup, cpu, memory) + err := b.Core.pluginRuntimeCatalog.Set(ctx, runtimeName, runtimeType, ociRuntime, cgroupParent, cpu, memory) if err != nil { return logical.ErrorResponse(err.Error()), nil } + default: + logical.ErrorResponse(fmt.Sprintf("%s is not a supported plugin runtime type", runtimeTypeStr)) } return nil, nil } @@ -776,8 +775,6 @@ func (b *SystemBackend) handlePluginRuntimeCatalogDelete(ctx context.Context, _ return logical.ErrorResponse("missing plugin runtime name"), nil } - // TODO validate name - runtimeTypeStr := d.Get("type").(string) if runtimeTypeStr == "" { return logical.ErrorResponse("missing plugin runtime type"), nil @@ -801,8 +798,6 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l return logical.ErrorResponse("missing plugin runtime name"), nil } - // TODO validate name - runtimeTypeStr := d.Get("type").(string) if runtimeTypeStr == "" { return logical.ErrorResponse("missing plugin runtime type"), nil @@ -813,20 +808,60 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l return logical.ErrorResponse(err.Error()), nil } - runner, err := b.Core.pluginRuntimeCatalog.Get(ctx, runtimeName, runtimeType) + conf, err := b.Core.pluginRuntimeCatalog.Get(ctx, runtimeName, runtimeType) if err != nil { return logical.ErrorResponse(err.Error()), nil } return &logical.Response{Data: map[string]interface{}{ - "name": runner.Name, - "type": runner.Type, - "oci_runtime": runner.OCIRuntime, - "parent_cgroup": runner.ParentCGroup, - "cpu": runner.CPU, - "memory": runner.Memory, + "name": conf.Name, + "type": conf.Type.String(), + "oci_runtime": conf.OCIRuntime, + "cgroup_parent": conf.CgroupParent, + "cpu": conf.CPU, + "memory": conf.Memory, }}, nil } +func (b *SystemBackend) handlePluginRuntimeCatalogTypedList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + runtimeType, err := consts.ParsePluginRuntimeType(d.Get("type").(string)) + if err != nil { + return nil, err + } + + runtimes, err := b.Core.pluginRuntimeCatalog.List(ctx, runtimeType) + if err != nil { + return nil, err + } + sort.Strings(runtimes) + return logical.ListResponse(runtimes), nil +} + +func (b *SystemBackend) handlePluginRuntimeCatalogUntypedList(ctx context.Context, _ *logical.Request, _ *framework.FieldData) (*logical.Response, error) { + data := make(map[string]interface{}) + for _, runtimeType := range consts.PluginRuntimeTypes { + if runtimeType == consts.PluginRuntimeTypeUnsupported { + continue + } + runtimes, err := b.Core.pluginRuntimeCatalog.List(ctx, runtimeType) + if err != nil { + return nil, err + } + if len(runtimes) > 0 { + sort.Strings(runtimes) + data[runtimeType.String()] = runtimes + } + } + + resp := &logical.Response{ + Data: map[string]interface{}{}, + } + if len(data) > 0 { + resp.Data["types"] = data + } + + return resp, nil +} + // handleAuditedHeaderUpdate creates or overwrites a header entry func (b *SystemBackend) handleAuditedHeaderUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { header := d.Get("header").(string) @@ -6000,31 +6035,48 @@ Each entry is of the form "key=value".`, "", }, "plugin-runtime-catalog": { - "", // TODO thy def. - "", // TODO thy def. + "Configures plugin runtimes", + ` +This path responds to the following HTTP methods. + LIST / + Returns a list of names of configured plugin runtimes. + + GET /<type>/<name> + Retrieve the metadata for the named plugin runtime. + + PUT /<type>/<name> + Add or update plugin runtime. + + DELETE /<type>/<name> + Delete the plugin runtime with the given name. + `, + }, + "plugin-runtime-catalog-list-all": { + "List all plugin runtimes in the catalog as a map of type to names.", + "", }, "plugin-runtime-catalog_name": { - "", // TODO thy def. + "The name of the plugin runtime", "", }, "plugin-runtime-catalog_type": { - "", // TODO thy def. + "The type of the plugin runtime", "", }, "plugin-runtime-catalog_oci-runtime": { - "", // TODO thy def. + "The OCI-compatible runtime (default \"runsc\")", "", }, - "plugin-runtime-catalog_parent-cgroup": { - "", // TODO thy def. + "plugin-runtime-catalog_cgroup-parent": { + "Optional parent cgroup for the container", "", }, "plugin-runtime-catalog_cpu": { - "", // TODO thy def. + "The limit of runtime CPU (default 0.1)", "", }, "plugin-runtime-catalog_memory": { - "", // TODO thy def. + "The limit of runtime memory in bytes (default 100000000)", "", }, "leases": { diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index 2b8c887916ca..f88bfbb5810d 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -2095,7 +2095,7 @@ func (b *SystemBackend) pluginsReloadPath() *framework.Path { func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { return &framework.Path{ - Pattern: "plugins/runtimes/catalog(/(?P<type>container))?/(?P<name>.+)", + Pattern: "plugins/runtimes/catalog/(?P<type>container)/" + framework.GenericNameRegex("name"), DisplayAttrs: &framework.DisplayAttributes{ OperationPrefix: "plugins-runtimes-catalog", @@ -2114,9 +2114,9 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_oci-runtime"][0]), }, - "parent_cgroup": { + "cgroup_parent": { Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_parent-cgroup"][0]), + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cgroup-parent"][0]), }, "cpu": { Type: framework.TypeFloat, @@ -2151,7 +2151,6 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { Responses: map[int][]framework.Response{ http.StatusOK: {{ Description: "OK", - Fields: map[string]*framework.FieldSchema{}, }}, }, Summary: "Remove the plugin runtime with the given name.", @@ -2181,9 +2180,9 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_oci-runtime"][0]), Required: true, }, - "parent_cgroup": { + "cgroup_parent": { Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_parent-cgroup"][0]), + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cgroup-parent"][0]), Required: true, }, "cpu": { @@ -2208,10 +2207,78 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { } } -// TODO thy -//func (b *SystemBackend) pluginsRuntimesCatalogListPaths() []*framework.Path { -// return []*framework.Path{} -//} +func (b *SystemBackend) pluginsRuntimesCatalogListPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "plugins/runtimes/catalog/(?P<type>container)/?$", + + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: "plugins-runtimes-catalog", + OperationVerb: "list", + OperationSuffix: "plugins-runtimes-with-type", + }, + + Fields: map[string]*framework.FieldSchema{ + "type": { + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_type"][0]), + }, + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ListOperation: &framework.PathOperation{ + Callback: b.handlePluginRuntimeCatalogTypedList, + Responses: map[int][]framework.Response{ + http.StatusOK: {{ + Description: "OK", + Fields: map[string]*framework.FieldSchema{ + "keys": { + Type: framework.TypeStringSlice, + Description: "List of plugin runtime names in the catalog", + Required: true, + }, + }, + }}, + }, + Summary: "List the plugin runtimes of a given type in the catalog.", + }, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["plugin-runtime-catalog"][0]), + HelpDescription: strings.TrimSpace(sysHelp["plugin-runtime-catalog"][1]), + }, + { + Pattern: "plugins/runtimes/catalog/?$", + + DisplayAttrs: &framework.DisplayAttributes{ + OperationPrefix: "plugins-runtimes-catalog", + OperationVerb: "list", + OperationSuffix: "plugins-runtimes", + }, + + Operations: map[logical.Operation]framework.OperationHandler{ + logical.ReadOperation: &framework.PathOperation{ + Callback: b.handlePluginRuntimeCatalogUntypedList, + Responses: map[int][]framework.Response{ + http.StatusOK: {{ + Description: "OK", + Fields: map[string]*framework.FieldSchema{ + "types": { + Type: framework.TypeMap, + Description: "Map of plugin runtime types to their plugin runtime names in the catalog", + Required: true, + }, + }, + }}, + }, + }, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["plugin-runtime-catalog-list-all"][0]), + HelpDescription: strings.TrimSpace(sysHelp["plugin-runtime-catalog-list-all"][1]), + }, + } +} func (b *SystemBackend) toolsPaths() []*framework.Path { return []*framework.Path{ diff --git a/vault/plugin_runtime_catalog.go b/vault/plugin_runtime_catalog.go index d11ca2afa2e1..008e276cd5c3 100644 --- a/vault/plugin_runtime_catalog.go +++ b/vault/plugin_runtime_catalog.go @@ -46,8 +46,8 @@ func (c *Core) setupPluginRuntimeCatalog(ctx context.Context) error { } // Get retrieves a plugin runtime with the specified name from the catalog -// It returns a PluginRuntimeRunner or an error if no plugin runtime was found. -func (c *PluginRuntimeCatalog) Get(ctx context.Context, name string, prt consts.PluginRuntimeType) (*pluginruntimeutil.PluginRuntimeRunner, error) { +// It returns a PluginRuntimeConfig or an error if no plugin runtime was found. +func (c *PluginRuntimeCatalog) Get(ctx context.Context, name string, prt consts.PluginRuntimeType) (*pluginruntimeutil.PluginRuntimeConfig, error) { storageKey := path.Join(prt.String(), name) c.lock.RLock() defer c.lock.RUnlock() @@ -58,7 +58,7 @@ func (c *PluginRuntimeCatalog) Get(ctx context.Context, name string, prt consts. if entry == nil { return nil, fmt.Errorf("failed to retrieve plugin %q %q: %w", prt.String(), name, err) } - runner := new(pluginruntimeutil.PluginRuntimeRunner) + runner := new(pluginruntimeutil.PluginRuntimeConfig) if err := jsonutil.DecodeJSON(entry.Value, runner); err != nil { return nil, fmt.Errorf("failed to decode plugin runtime entry: %w", err) } @@ -73,11 +73,11 @@ func (c *PluginRuntimeCatalog) Set(ctx context.Context, name string, prt consts. c.lock.Lock() defer c.lock.Unlock() - entry := &pluginruntimeutil.PluginRuntimeRunner{ + entry := &pluginruntimeutil.PluginRuntimeConfig{ Name: name, Type: prt, OCIRuntime: ociRuntime, - ParentCGroup: parentCGroup, + CgroupParent: parentCGroup, CPU: cpu, Memory: memory, } @@ -132,7 +132,7 @@ func (c *PluginRuntimeCatalog) List(ctx context.Context, prt consts.PluginRuntim continue } - runner := new(pluginruntimeutil.PluginRuntimeRunner) + runner := new(pluginruntimeutil.PluginRuntimeConfig) if err := jsonutil.DecodeJSON(entry.Value, runner); err != nil { return nil, fmt.Errorf("failed to decode plugin runtime entry: %w", err) } diff --git a/vault/plugin_runtime_catalog_test.go b/vault/plugin_runtime_catalog_test.go index 0fada7832d3c..ce6558a2077b 100644 --- a/vault/plugin_runtime_catalog_test.go +++ b/vault/plugin_runtime_catalog_test.go @@ -8,7 +8,6 @@ import ( "reflect" "testing" - "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" ) @@ -16,17 +15,16 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) { core, _, _ := TestCoreUnsealed(t) ctx := context.Background() - expected := &pluginruntimeutil.PluginRuntimeRunner{ + expected := &pluginruntimeutil.PluginRuntimeConfig{ Name: "gvisor", - Type: consts.PluginRuntimeTypeContainer, OCIRuntime: "runsc", - ParentCGroup: "/cpulimit-cgroup/", + CgroupParent: "/cpulimit/", CPU: 1, Memory: 10000, } // Set new plugin runtime - err := core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.ParentCGroup, expected.CPU, expected.Memory) + err := core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.CgroupParent, expected.CPU, expected.Memory) if err != nil { t.Fatalf("err: %v", err) } @@ -41,10 +39,10 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) { } // Set existing plugin runtime - expected.ParentCGroup = "memorylimit-cgroup" + expected.CgroupParent = "memorylimit-cgroup" expected.CPU = 0.5 expected.Memory = 5000 - err = core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.ParentCGroup, expected.CPU, expected.Memory) + err = core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.CgroupParent, expected.CPU, expected.Memory) if err != nil { t.Fatalf("err: %v", err) } From bb3225479ca8e07d2b2be706e5a6e97e58b64535 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Tue, 29 Aug 2023 08:15:52 -0700 Subject: [PATCH 04/20] change runner.go to config.go --- sdk/helper/pluginruntimeutil/{runner.go => config.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sdk/helper/pluginruntimeutil/{runner.go => config.go} (100%) diff --git a/sdk/helper/pluginruntimeutil/runner.go b/sdk/helper/pluginruntimeutil/config.go similarity index 100% rename from sdk/helper/pluginruntimeutil/runner.go rename to sdk/helper/pluginruntimeutil/config.go From 5107640e759aaa772e6a8b6ebee44b1ff069b8ec Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Tue, 29 Aug 2023 08:16:45 -0700 Subject: [PATCH 05/20] wip plugin runtime CRUD test --- vault/logical_system_test.go | 136 +++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 7d5ce33a4714..b05b20ee02d4 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -5884,3 +5884,139 @@ func TestSystemBackend_ReadExperiments(t *testing.T) { }) } } + +func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { + b := testSystemBackend(t) + + // Register the plugin runtime + req := logical.TestRequest(t, logical.UpdateOperation, "plugins/runtimes/catalog/container/foo") + req.Data = map[string]interface{}{ + "oci_runtime": "some-oci-runtime", + "cgroup_parent": "/cpulimit/", + "cpu": 1, + "memory": 10000, + } + + resp, err := b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v %#v", err, resp) + } + if resp != nil && (resp.IsError() || len(resp.Data) > 0) { + t.Fatalf("bad: %#v", resp) + } + + // validate the response structure for plugin container runtime named foo + schema.ValidateResponse( + t, + schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation), + resp, + true, + ) + + // Read the plugin runtime + req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container/foo") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + + // validate the response structure for plugin container runtime named foo + schema.ValidateResponse( + t, + schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation), + resp, + true, + ) + + exp := map[string]interface{}{ + "name": "foo", + "type": "container", + "oci_runtime": "some-oci-runtime", + "cgroup_parent": "/cpulimit/", + "cpu": 1, + "memory": 10000, + } + if !reflect.DeepEqual(resp.Data, exp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + } + + // List the plugin runtimes of container type + req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + + exp = map[string]interface{}{ + "keys": []string{"foo"}, + } + if !reflect.DeepEqual(resp.Data, exp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + } + + // List the plugin runtimes (untyped or all) + req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + + exp = map[string]interface{}{ + "type": map[string][]string{"container": {"foo"}}, + } + if !reflect.DeepEqual(resp.Data, exp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + } + + // Delete the plugin runtime + req = logical.TestRequest(t, logical.DeleteOperation, "plugins/runtimes/catalog/container/foo") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + if resp != nil { + t.Fatalf("bad: %#v", resp) + } + + // validate the response structure for plugin container runtime named foo + schema.ValidateResponse( + t, + schema.GetResponseSchema(t, b.(*SystemBackend).Route(req.Path), req.Operation), + resp, + true, + ) + + // Read the plugin runtime (deleted) + req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container/foo") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + if resp != nil { + t.Fatalf("bad: %#v", resp) + } + + // List the plugin runtimes (deleted) + req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + + exp = map[string]interface{}{} + if !reflect.DeepEqual(resp.Data, exp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + } + + // List the plugin runtimes (untyped or all) + req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog") + resp, err = b.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + + exp = map[string]interface{}{} + if !reflect.DeepEqual(resp.Data, exp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + } +} From d2b890385f74c0c15e11b97f7aa3fb5f9916ab15 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Tue, 29 Aug 2023 17:41:00 -0700 Subject: [PATCH 06/20] fix plugin runtime CRUD test failure --- sdk/helper/pluginruntimeutil/config.go | 2 +- vault/logical_system.go | 16 +++++++++++++--- vault/logical_system_paths.go | 6 +++++- vault/logical_system_test.go | 18 +++++++++--------- vault/plugin_runtime_catalog.go | 2 +- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/sdk/helper/pluginruntimeutil/config.go b/sdk/helper/pluginruntimeutil/config.go index 1deaa6d195ba..16f97c9ead11 100644 --- a/sdk/helper/pluginruntimeutil/config.go +++ b/sdk/helper/pluginruntimeutil/config.go @@ -15,5 +15,5 @@ type PluginRuntimeConfig struct { OCIRuntime string `json:"oci_runtime" structs:"oci_runtime"` CgroupParent string `json:"cgroup_parent" structs:"cgroup_parent"` CPU float32 `json:"cpu" structs:"cpu"` - Memory uint64 `json:"memory" structs:"memory"` + Memory int64 `json:"memory" structs:"memory"` } diff --git a/vault/logical_system.go b/vault/logical_system.go index 27d192c082d9..e4936af185c7 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -752,10 +752,16 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ } cgroupParent := d.Get("cgroup_parent").(string) cpu := float32(d.Get("cpu").(float64)) + if cpu < 0 { + return logical.ErrorResponse("runtime cpu in bytes must be greater than 0"), nil + } if cpu == 0 { cpu = pluginruntimeutil.DefaultCPU } - memory := uint64(d.Get("memory").(int64)) + memory := d.Get("memory").(int64) + if memory < 0 { + return logical.ErrorResponse("runtime memory in bytes must be greater than 0"), nil + } if memory == 0 { memory = pluginruntimeutil.DefaultMemory } @@ -787,7 +793,7 @@ func (b *SystemBackend) handlePluginRuntimeCatalogDelete(ctx context.Context, _ err = b.Core.pluginRuntimeCatalog.Delete(ctx, runtimeName, runtimeType) if err != nil { - return logical.ErrorResponse(err.Error()), nil + return nil, err } return nil, nil } @@ -810,8 +816,12 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l conf, err := b.Core.pluginRuntimeCatalog.Get(ctx, runtimeName, runtimeType) if err != nil { - return logical.ErrorResponse(err.Error()), nil + return nil, err + } + if conf == nil { + return nil, nil } + return &logical.Response{Data: map[string]interface{}{ "name": conf.Name, "type": conf.Type.String(), diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index f88bfbb5810d..df906ef3dfbf 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" "github.com/hashicorp/vault/sdk/logical" ) @@ -2113,6 +2114,7 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { "oci_runtime": { Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_oci-runtime"][0]), + Default: pluginruntimeutil.DefaultOCIRuntime, }, "cgroup_parent": { Type: framework.TypeString, @@ -2121,10 +2123,12 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { "cpu": { Type: framework.TypeFloat, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), + Default: pluginruntimeutil.DefaultCPU, }, "memory": { Type: framework.TypeInt64, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory"][0]), + Default: pluginruntimeutil.DefaultMemory, }, }, @@ -2257,7 +2261,7 @@ func (b *SystemBackend) pluginsRuntimesCatalogListPaths() []*framework.Path { }, Operations: map[logical.Operation]framework.OperationHandler{ - logical.ReadOperation: &framework.PathOperation{ + logical.ListOperation: &framework.PathOperation{ Callback: b.handlePluginRuntimeCatalogUntypedList, Responses: map[int][]framework.Response{ http.StatusOK: {{ diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index b05b20ee02d4..a0bf1f385646 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -5933,15 +5933,15 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { "type": "container", "oci_runtime": "some-oci-runtime", "cgroup_parent": "/cpulimit/", - "cpu": 1, - "memory": 10000, + "cpu": float32(1), + "memory": int64(10000), } if !reflect.DeepEqual(resp.Data, exp) { t.Fatalf("got: %#v expect: %#v", resp.Data, exp) } // List the plugin runtimes of container type - req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container") + req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog/container") resp, err = b.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) @@ -5955,14 +5955,14 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { } // List the plugin runtimes (untyped or all) - req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog") + req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog") resp, err = b.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } exp = map[string]interface{}{ - "type": map[string][]string{"container": {"foo"}}, + "types": map[string]interface{}{"container": []string{"foo"}}, } if !reflect.DeepEqual(resp.Data, exp) { t.Fatalf("got: %#v expect: %#v", resp.Data, exp) @@ -5989,15 +5989,15 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { // Read the plugin runtime (deleted) req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container/foo") resp, err = b.HandleRequest(namespace.RootContext(nil), req) - if err != nil { - t.Fatalf("err: %v", err) + if err == nil { + t.Fatal("expected a read error after the runtime was deleted") } if resp != nil { t.Fatalf("bad: %#v", resp) } // List the plugin runtimes (deleted) - req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog/container") + req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog/container") resp, err = b.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) @@ -6009,7 +6009,7 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { } // List the plugin runtimes (untyped or all) - req = logical.TestRequest(t, logical.ReadOperation, "plugins/runtimes/catalog") + req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog") resp, err = b.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) diff --git a/vault/plugin_runtime_catalog.go b/vault/plugin_runtime_catalog.go index 008e276cd5c3..28720810f27f 100644 --- a/vault/plugin_runtime_catalog.go +++ b/vault/plugin_runtime_catalog.go @@ -69,7 +69,7 @@ func (c *PluginRuntimeCatalog) Get(ctx context.Context, name string, prt consts. } // Set registers a new plugin with the catalog, or updates an existing plugin runtime -func (c *PluginRuntimeCatalog) Set(ctx context.Context, name string, prt consts.PluginRuntimeType, ociRuntime string, parentCGroup string, cpu float32, memory uint64) error { +func (c *PluginRuntimeCatalog) Set(ctx context.Context, name string, prt consts.PluginRuntimeType, ociRuntime string, parentCGroup string, cpu float32, memory int64) error { c.lock.Lock() defer c.lock.Unlock() From 8352ecd9d08e469fb51fc690d77984b912e5e089 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 10:20:54 -0700 Subject: [PATCH 07/20] add copyright headers --- api/sys_plugins_runtimes_test.go | 3 +++ sdk/helper/pluginruntimeutil/config.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/api/sys_plugins_runtimes_test.go b/api/sys_plugins_runtimes_test.go index d3d06af0da09..312f5e670c4c 100644 --- a/api/sys_plugins_runtimes_test.go +++ b/api/sys_plugins_runtimes_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package api import ( diff --git a/sdk/helper/pluginruntimeutil/config.go b/sdk/helper/pluginruntimeutil/config.go index 16f97c9ead11..f492429f999e 100644 --- a/sdk/helper/pluginruntimeutil/config.go +++ b/sdk/helper/pluginruntimeutil/config.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package pluginruntimeutil import "github.com/hashicorp/vault/sdk/helper/consts" From 49a26e04b0d429713375439ad4c7292acbe53867 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 10:57:19 -0700 Subject: [PATCH 08/20] change cpu to int64 type and refactor PluginRuntimeCatalog.Set() --- api/sys_plugins_runtimes.go | 32 +++++++++++++------------- sdk/helper/pluginruntimeutil/config.go | 4 ++-- vault/logical_system.go | 15 ++++++++---- vault/logical_system_paths.go | 4 ++-- vault/logical_system_test.go | 2 +- vault/plugin_runtime_catalog.go | 17 ++++---------- vault/plugin_runtime_catalog_test.go | 6 ++--- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index c68facd22e75..5d66cff31683 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -13,12 +13,12 @@ import ( ) type PluginRuntimeDetails struct { - Type string `json:"type"` - Name string `json:"name"` - OCIRuntime string `json:"oci_runtime"` - CgroupParent string `json:"cgroup_parent"` - CPU float32 `json:"cpu"` - Memory uint64 `json:"memory"` + Type string `json:"type"` + Name string `json:"name"` + OCIRuntime string `json:"oci_runtime"` + CgroupParent string `json:"cgroup_parent"` + CPU int64 `json:"cpu"` + Memory int64 `json:"memory"` } // ListPluginRuntimesInput is used as input to the ListPluginRuntimes function. @@ -43,12 +43,12 @@ type GetPluginRuntimeInput struct { // GetPluginRuntimeResponse is the response from the GetPluginRuntime call. type GetPluginRuntimeResponse struct { - Type string `json:"type"` - Name string `json:"name"` - OCIRuntime string `json:"oci_runtime"` - CgroupParent string `json:"cgroup_parent"` - CPU float32 `json:"cpu"` - Memory uint64 `json:"memory"` + Type string `json:"type"` + Name string `json:"name"` + OCIRuntime string `json:"oci_runtime"` + CgroupParent string `json:"cgroup_parent"` + CPU int64 `json:"cpu"` + Memory int64 `json:"memory"` } // GetPluginRuntime wraps GetPluginRuntimeWithContext using context.Background. @@ -88,10 +88,10 @@ type RegisterPluginRuntimeInput struct { // Type of the plugin. Required. Type PluginRuntimeType `json:"type"` - OCIRuntime string `json:"oci_runtime,omitempty"` - CgroupParent string `json:"cgroup_parent,omitempty"` - CPU float32 `json:"cpu,omitempty"` - Memory uint64 `json:"memory,omitempty"` + OCIRuntime string `json:"oci_runtime,omitempty"` + CgroupParent string `json:"cgroup_parent,omitempty"` + CPU int64 `json:"cpu,omitempty"` + Memory int64 `json:"memory,omitempty"` } // RegisterPluginRuntime wraps RegisterPluginWithContext using context.Background. diff --git a/sdk/helper/pluginruntimeutil/config.go b/sdk/helper/pluginruntimeutil/config.go index f492429f999e..b72fa1ead785 100644 --- a/sdk/helper/pluginruntimeutil/config.go +++ b/sdk/helper/pluginruntimeutil/config.go @@ -7,7 +7,7 @@ import "github.com/hashicorp/vault/sdk/helper/consts" const ( DefaultOCIRuntime = "runsc" - DefaultCPU = 0.1 + DefaultCPU = 1 DefaultMemory = 100000000 ) @@ -17,6 +17,6 @@ type PluginRuntimeConfig struct { Type consts.PluginRuntimeType `json:"type" structs:"type"` OCIRuntime string `json:"oci_runtime" structs:"oci_runtime"` CgroupParent string `json:"cgroup_parent" structs:"cgroup_parent"` - CPU float32 `json:"cpu" structs:"cpu"` + CPU int64 `json:"cpu" structs:"cpu"` Memory int64 `json:"memory" structs:"memory"` } diff --git a/vault/logical_system.go b/vault/logical_system.go index a0f2f4064ccf..1c3b3ef11619 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -752,9 +752,9 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ ociRuntime = pluginruntimeutil.DefaultOCIRuntime } cgroupParent := d.Get("cgroup_parent").(string) - cpu := float32(d.Get("cpu").(float64)) + cpu := d.Get("cpu").(int64) if cpu < 0 { - return logical.ErrorResponse("runtime cpu in bytes must be greater than 0"), nil + return logical.ErrorResponse("runtime cpu must be greater than 0"), nil } if cpu == 0 { cpu = pluginruntimeutil.DefaultCPU @@ -766,8 +766,15 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ if memory == 0 { memory = pluginruntimeutil.DefaultMemory } - err := b.Core.pluginRuntimeCatalog.Set(ctx, runtimeName, runtimeType, ociRuntime, cgroupParent, cpu, memory) - if err != nil { + if err = b.Core.pluginRuntimeCatalog.Set(ctx, + &pluginruntimeutil.PluginRuntimeConfig{ + Name: runtimeName, + Type: runtimeType, + OCIRuntime: ociRuntime, + CgroupParent: cgroupParent, + CPU: cpu, + Memory: memory, + }); err != nil { return logical.ErrorResponse(err.Error()), nil } default: diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index df906ef3dfbf..c03061dde5a9 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -2121,7 +2121,7 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cgroup-parent"][0]), }, "cpu": { - Type: framework.TypeFloat, + Type: framework.TypeInt64, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), Default: pluginruntimeutil.DefaultCPU, }, @@ -2190,7 +2190,7 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { Required: true, }, "cpu": { - Type: framework.TypeFloat, + Type: framework.TypeInt64, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), Required: true, }, diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index a0bf1f385646..8271a4c513f0 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -5933,7 +5933,7 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { "type": "container", "oci_runtime": "some-oci-runtime", "cgroup_parent": "/cpulimit/", - "cpu": float32(1), + "cpu": int64(1), "memory": int64(10000), } if !reflect.DeepEqual(resp.Data, exp) { diff --git a/vault/plugin_runtime_catalog.go b/vault/plugin_runtime_catalog.go index 28720810f27f..551e7c47b366 100644 --- a/vault/plugin_runtime_catalog.go +++ b/vault/plugin_runtime_catalog.go @@ -69,26 +69,20 @@ func (c *PluginRuntimeCatalog) Get(ctx context.Context, name string, prt consts. } // Set registers a new plugin with the catalog, or updates an existing plugin runtime -func (c *PluginRuntimeCatalog) Set(ctx context.Context, name string, prt consts.PluginRuntimeType, ociRuntime string, parentCGroup string, cpu float32, memory int64) error { +func (c *PluginRuntimeCatalog) Set(ctx context.Context, conf *pluginruntimeutil.PluginRuntimeConfig) error { c.lock.Lock() defer c.lock.Unlock() - entry := &pluginruntimeutil.PluginRuntimeConfig{ - Name: name, - Type: prt, - OCIRuntime: ociRuntime, - CgroupParent: parentCGroup, - CPU: cpu, - Memory: memory, + if conf == nil { + return fmt.Errorf("plugin runtime config reference is nil") } - // TODO what happens to the running existing container with(out) running plugins. Need to shut it down before setting it? - buf, err := json.Marshal(entry) + buf, err := json.Marshal(conf) if err != nil { return fmt.Errorf("failed to encode plugin entry: %w", err) } - storageKey := path.Join(prt.String(), name) + storageKey := path.Join(conf.Type.String(), conf.Name) logicalEntry := logical.StorageEntry{ Key: storageKey, Value: buf, @@ -112,7 +106,6 @@ func (c *PluginRuntimeCatalog) Delete(ctx context.Context, name string, prt cons return ErrPluginRuntimeNotFound } - // TODO what happens to the running existing container with(out) running plugins. Need to shut it down before setting it? return c.catalogView.Delete(ctx, storageKey) } diff --git a/vault/plugin_runtime_catalog_test.go b/vault/plugin_runtime_catalog_test.go index ce6558a2077b..b576cfd61ef7 100644 --- a/vault/plugin_runtime_catalog_test.go +++ b/vault/plugin_runtime_catalog_test.go @@ -24,7 +24,7 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) { } // Set new plugin runtime - err := core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.CgroupParent, expected.CPU, expected.Memory) + err := core.pluginRuntimeCatalog.Set(ctx, expected) if err != nil { t.Fatalf("err: %v", err) } @@ -40,9 +40,9 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) { // Set existing plugin runtime expected.CgroupParent = "memorylimit-cgroup" - expected.CPU = 0.5 + expected.CPU = 2 expected.Memory = 5000 - err = core.pluginRuntimeCatalog.Set(ctx, expected.Name, expected.Type, expected.OCIRuntime, expected.CgroupParent, expected.CPU, expected.Memory) + err = core.pluginRuntimeCatalog.Set(ctx, expected) if err != nil { t.Fatalf("err: %v", err) } From 66201a14c73b7016a0cb3e4137367575a72dea4e Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 11:27:06 -0700 Subject: [PATCH 09/20] faithfully represent the data passed to us in storage --- vault/logical_system.go | 23 --------------------- vault/logical_system_paths.go | 38 ----------------------------------- vault/logical_system_test.go | 26 ------------------------ 3 files changed, 87 deletions(-) diff --git a/vault/logical_system.go b/vault/logical_system.go index 1c3b3ef11619..927c940a42bd 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -748,24 +748,15 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ switch runtimeType { case consts.PluginRuntimeTypeContainer: ociRuntime := d.Get("oci_runtime").(string) - if ociRuntime == "" { - ociRuntime = pluginruntimeutil.DefaultOCIRuntime - } cgroupParent := d.Get("cgroup_parent").(string) cpu := d.Get("cpu").(int64) if cpu < 0 { return logical.ErrorResponse("runtime cpu must be greater than 0"), nil } - if cpu == 0 { - cpu = pluginruntimeutil.DefaultCPU - } memory := d.Get("memory").(int64) if memory < 0 { return logical.ErrorResponse("runtime memory in bytes must be greater than 0"), nil } - if memory == 0 { - memory = pluginruntimeutil.DefaultMemory - } if err = b.Core.pluginRuntimeCatalog.Set(ctx, &pluginruntimeutil.PluginRuntimeConfig{ Name: runtimeName, @@ -840,20 +831,6 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l }}, nil } -func (b *SystemBackend) handlePluginRuntimeCatalogTypedList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - runtimeType, err := consts.ParsePluginRuntimeType(d.Get("type").(string)) - if err != nil { - return nil, err - } - - runtimes, err := b.Core.pluginRuntimeCatalog.List(ctx, runtimeType) - if err != nil { - return nil, err - } - sort.Strings(runtimes) - return logical.ListResponse(runtimes), nil -} - func (b *SystemBackend) handlePluginRuntimeCatalogUntypedList(ctx context.Context, _ *logical.Request, _ *framework.FieldData) (*logical.Response, error) { data := make(map[string]interface{}) for _, runtimeType := range consts.PluginRuntimeTypes { diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index c03061dde5a9..1114c2994c29 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -2213,44 +2213,6 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { func (b *SystemBackend) pluginsRuntimesCatalogListPaths() []*framework.Path { return []*framework.Path{ - { - Pattern: "plugins/runtimes/catalog/(?P<type>container)/?$", - - DisplayAttrs: &framework.DisplayAttributes{ - OperationPrefix: "plugins-runtimes-catalog", - OperationVerb: "list", - OperationSuffix: "plugins-runtimes-with-type", - }, - - Fields: map[string]*framework.FieldSchema{ - "type": { - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_type"][0]), - }, - }, - - Operations: map[logical.Operation]framework.OperationHandler{ - logical.ListOperation: &framework.PathOperation{ - Callback: b.handlePluginRuntimeCatalogTypedList, - Responses: map[int][]framework.Response{ - http.StatusOK: {{ - Description: "OK", - Fields: map[string]*framework.FieldSchema{ - "keys": { - Type: framework.TypeStringSlice, - Description: "List of plugin runtime names in the catalog", - Required: true, - }, - }, - }}, - }, - Summary: "List the plugin runtimes of a given type in the catalog.", - }, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["plugin-runtime-catalog"][0]), - HelpDescription: strings.TrimSpace(sysHelp["plugin-runtime-catalog"][1]), - }, { Pattern: "plugins/runtimes/catalog/?$", diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 8271a4c513f0..608f00e2364d 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -5940,20 +5940,6 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { t.Fatalf("got: %#v expect: %#v", resp.Data, exp) } - // List the plugin runtimes of container type - req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog/container") - resp, err = b.HandleRequest(namespace.RootContext(nil), req) - if err != nil { - t.Fatalf("err: %v", err) - } - - exp = map[string]interface{}{ - "keys": []string{"foo"}, - } - if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("got: %#v expect: %#v", resp.Data, exp) - } - // List the plugin runtimes (untyped or all) req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog") resp, err = b.HandleRequest(namespace.RootContext(nil), req) @@ -5996,18 +5982,6 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { t.Fatalf("bad: %#v", resp) } - // List the plugin runtimes (deleted) - req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog/container") - resp, err = b.HandleRequest(namespace.RootContext(nil), req) - if err != nil { - t.Fatalf("err: %v", err) - } - - exp = map[string]interface{}{} - if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("got: %#v expect: %#v", resp.Data, exp) - } - // List the plugin runtimes (untyped or all) req = logical.TestRequest(t, logical.ListOperation, "plugins/runtimes/catalog") resp, err = b.HandleRequest(namespace.RootContext(nil), req) From d06ef06f425bf738ba269778869f5c6fc41fab41 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 11:33:26 -0700 Subject: [PATCH 10/20] remove ListPluginRuntimesWithContext and use ListPluginRuntimes with context.Context parameter --- api/sys_plugins_runtimes.go | 9 ++------- api/sys_plugins_runtimes_test.go | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index 5d66cff31683..7e9b59feba1f 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -147,14 +147,9 @@ func (c *Sys) DeregisterPluginRuntimeWithContext(ctx context.Context, i *Deregis return err } -// ListPluginRuntimes wraps ListPluginRuntimesWithContext using context.Background. -func (c *Sys) ListPluginRuntimes(i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { - return c.ListPluginRuntimesWithContext(context.Background(), i) -} - -// ListPluginRuntimesWithContext lists all plugin runtimes in the catalog and returns their names as a +// ListPluginRuntimes lists all plugin runtimes in the catalog and returns their names as a // list of strings. -func (c *Sys) ListPluginRuntimesWithContext(ctx context.Context, i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { +func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) defer cancelFunc() diff --git a/api/sys_plugins_runtimes_test.go b/api/sys_plugins_runtimes_test.go index 312f5e670c4c..6a2a2b98090b 100644 --- a/api/sys_plugins_runtimes_test.go +++ b/api/sys_plugins_runtimes_test.go @@ -119,7 +119,7 @@ func TestListPluginRuntimeTyped(t *testing.T) { Type: tc.runtimeType, } - list, err := client.Sys().ListPluginRuntimesWithContext(context.Background(), &input) + list, err := client.Sys().ListPluginRuntimes(context.Background(), &input) if tc.expectedErrNil && err != nil { t.Fatal(err) } @@ -158,7 +158,7 @@ func TestListPluginRuntimeUntyped(t *testing.T) { t.Fatal(err) } - info, err := client.Sys().ListPluginRuntimesWithContext(context.Background(), nil) + info, err := client.Sys().ListPluginRuntimes(context.Background(), nil) if tc.expectedErrNil && err != nil { t.Fatal(err) } From 8ebbdfba3fd1d30871a01daff89655646bfacc20 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 17:20:15 -0700 Subject: [PATCH 11/20] list API returns a slice of plugin runtimes in response data --- api/sys_plugins_runtimes.go | 113 ++++++++++++--------------- api/sys_plugins_runtimes_test.go | 80 +++++++++++++++++-- vault/logical_system.go | 27 +++++-- vault/logical_system_paths.go | 8 +- vault/logical_system_test.go | 52 +++++++----- vault/plugin_runtime_catalog.go | 12 +-- vault/plugin_runtime_catalog_test.go | 12 +-- 7 files changed, 188 insertions(+), 116 deletions(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index 7e9b59feba1f..0c605f55a3a9 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -12,27 +12,6 @@ import ( "github.com/mitchellh/mapstructure" ) -type PluginRuntimeDetails struct { - Type string `json:"type"` - Name string `json:"name"` - OCIRuntime string `json:"oci_runtime"` - CgroupParent string `json:"cgroup_parent"` - CPU int64 `json:"cpu"` - Memory int64 `json:"memory"` -} - -// ListPluginRuntimesInput is used as input to the ListPluginRuntimes function. -type ListPluginRuntimesInput struct { - // Type of the plugin. Required. - Type PluginRuntimeType `json:"type"` -} - -// ListPluginRuntimesResponse is the response from the ListPluginRuntimes call. -type ListPluginRuntimesResponse struct { - // RuntimesByType is the list of plugin runtimes by type. - RuntimesByType map[PluginRuntimeType][]string `json:"types"` -} - // GetPluginRuntimeInput is used as input to the GetPluginRuntime function. type GetPluginRuntimeInput struct { Name string `json:"-"` @@ -51,13 +30,8 @@ type GetPluginRuntimeResponse struct { Memory int64 `json:"memory"` } -// GetPluginRuntime wraps GetPluginRuntimeWithContext using context.Background. -func (c *Sys) GetPluginRuntime(i *GetPluginRuntimeInput) (*GetPluginRuntimeResponse, error) { - return c.GetPluginRuntimeWithContext(context.Background(), i) -} - -// GetPluginRuntimeWithContext retrieves information about the plugin. -func (c *Sys) GetPluginRuntimeWithContext(ctx context.Context, i *GetPluginRuntimeInput) (*GetPluginRuntimeResponse, error) { +// GetPluginRuntime retrieves information about the plugin. +func (c *Sys) GetPluginRuntime(ctx context.Context, i *GetPluginRuntimeInput) (*GetPluginRuntimeResponse, error) { ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) defer cancelFunc() @@ -94,13 +68,8 @@ type RegisterPluginRuntimeInput struct { Memory int64 `json:"memory,omitempty"` } -// RegisterPluginRuntime wraps RegisterPluginWithContext using context.Background. -func (c *Sys) RegisterPluginRuntime(i *RegisterPluginRuntimeInput) error { - return c.RegisterPluginRuntimeWithContext(context.Background(), i) -} - -// RegisterPluginRuntimeWithContext registers the plugin with the given information. -func (c *Sys) RegisterPluginRuntimeWithContext(ctx context.Context, i *RegisterPluginRuntimeInput) error { +// RegisterPluginRuntime registers the plugin with the given information. +func (c *Sys) RegisterPluginRuntime(ctx context.Context, i *RegisterPluginRuntimeInput) error { ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) defer cancelFunc() @@ -127,14 +96,9 @@ type DeregisterPluginRuntimeInput struct { Type PluginRuntimeType `json:"type"` } -// DeregisterPluginRuntime wraps DeregisterPluginRuntimeWithContext using context.Background. -func (c *Sys) DeregisterPluginRuntime(i *DeregisterPluginRuntimeInput) error { - return c.DeregisterPluginRuntimeWithContext(context.Background(), i) -} - -// DeregisterPluginRuntimeWithContext removes the plugin with the given name from the plugin +// DeregisterPluginRuntime removes the plugin with the given name from the plugin // catalog. -func (c *Sys) DeregisterPluginRuntimeWithContext(ctx context.Context, i *DeregisterPluginRuntimeInput) error { +func (c *Sys) DeregisterPluginRuntime(ctx context.Context, i *DeregisterPluginRuntimeInput) error { ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) defer cancelFunc() @@ -147,6 +111,27 @@ func (c *Sys) DeregisterPluginRuntimeWithContext(ctx context.Context, i *Deregis return err } +type PluginRuntimeDetails struct { + Type string `json:"type"` + Name string `json:"name"` + OCIRuntime string `json:"oci_runtime"` + CgroupParent string `json:"cgroup_parent"` + CPU int64 `json:"cpu"` + Memory int64 `json:"memory"` +} + +// ListPluginRuntimesInput is used as input to the ListPluginRuntimes function. +type ListPluginRuntimesInput struct { + // Type of the plugin. Required. + Type PluginRuntimeType `json:"type"` +} + +// ListPluginRuntimesResponse is the response from the ListPluginRuntimes call. +type ListPluginRuntimesResponse struct { + // RuntimesByType is the list of plugin runtimes by type. + Runtimes []PluginRuntimeDetails `json:"runtimes"` +} + // ListPluginRuntimes lists all plugin runtimes in the catalog and returns their names as a // list of strings. func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { @@ -173,40 +158,40 @@ func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput if secret == nil || secret.Data == nil { return nil, errors.New("data from server response is empty") } + if _, ok := secret.Data["runtimes"]; !ok { + return nil, fmt.Errorf("data from server response does not contain runtimes") + } + + runtimesRaw, ok := secret.Data["runtimes"].([]interface{}) + if !ok { + return nil, fmt.Errorf("unable to parse runtimes") + } result := &ListPluginRuntimesResponse{ - RuntimesByType: make(map[PluginRuntimeType][]string), + Runtimes: []PluginRuntimeDetails{}, + } + var runtimes []PluginRuntimeDetails + for _, runtimeRaw := range runtimesRaw { + var runtime PluginRuntimeDetails + if err = mapstructure.Decode(runtimeRaw, &runtime); err != nil { + return nil, err + } + runtimes = append(runtimes, runtime) } // return all runtimes in the catalog if i == nil { - for _, runtimeType := range PluginRuntimeTypes { - runtimesRaw, ok := secret.Data[runtimeType.String()] - if !ok { - continue - } - - var tmp []string - if err = mapstructure.Decode(runtimesRaw, &tmp); err != nil { - return nil, err - } - result.RuntimesByType[runtimeType] = tmp - } + result.Runtimes = runtimes return result, nil } switch i.Type { default: - runtimesRaw, ok := secret.Data[i.Type.String()] - if !ok { - return nil, fmt.Errorf("no %s entry in returned data", i.Type.String()) - } - - var tmp []string - if err := mapstructure.Decode(runtimesRaw, &tmp); err != nil { - return nil, err + for _, runtime := range runtimes { + if runtime.Type == i.Type.String() { + result.Runtimes = append(result.Runtimes, runtime) + } } - result.RuntimesByType[i.Type] = tmp } return result, nil } diff --git a/api/sys_plugins_runtimes_test.go b/api/sys_plugins_runtimes_test.go index 6a2a2b98090b..58a61eb869f3 100644 --- a/api/sys_plugins_runtimes_test.go +++ b/api/sys_plugins_runtimes_test.go @@ -22,7 +22,7 @@ func TestRegisterPluginRuntime(t *testing.T) { t.Fatal(err) } - err = client.Sys().RegisterPluginRuntimeWithContext(context.Background(), &RegisterPluginRuntimeInput{ + err = client.Sys().RegisterPluginRuntime(context.Background(), &RegisterPluginRuntimeInput{ Name: "gvisor", Type: PluginRuntimeTypeContainer, OCIRuntime: "runsc", @@ -68,7 +68,7 @@ func TestGetPluginRuntime(t *testing.T) { Type: PluginRuntimeTypeContainer, } - info, err := client.Sys().GetPluginRuntimeWithContext(context.Background(), &input) + info, err := client.Sys().GetPluginRuntime(context.Background(), &input) if err != nil { t.Fatal(err) } @@ -91,8 +91,15 @@ func TestListPluginRuntimeTyped(t *testing.T) { runtimeType: PluginRuntimeTypeContainer, body: listPluginRuntimeTypedResponse, expectedResponse: &ListPluginRuntimesResponse{ - RuntimesByType: map[PluginRuntimeType][]string{ - PluginRuntimeTypeContainer: {"gvisor"}, + Runtimes: []PluginRuntimeDetails{ + { + Type: "container", + Name: "gvisor", + OCIRuntime: "runsc", + CgroupParent: "/cpulimit/", + CPU: 1, + Memory: 10000, + }, }, }, expectedErrNil: true, @@ -140,8 +147,31 @@ func TestListPluginRuntimeUntyped(t *testing.T) { { body: listPluginRuntimeUntypedResponse, expectedResponse: &ListPluginRuntimesResponse{ - RuntimesByType: map[PluginRuntimeType][]string{ - PluginRuntimeTypeContainer: {"gvisor", "foo", "bar"}, + Runtimes: []PluginRuntimeDetails{ + { + Type: "container", + Name: "gvisor", + OCIRuntime: "runsc", + CgroupParent: "/cpulimit/", + CPU: 1, + Memory: 10000, + }, + { + Type: "container", + Name: "foo", + OCIRuntime: "otherociruntime", + CgroupParent: "/memorylimit/", + CPU: 2, + Memory: 20000, + }, + { + Type: "container", + Name: "bar", + OCIRuntime: "otherociruntime", + CgroupParent: "/cpulimit/", + CPU: 3, + Memory: 30000, + }, }, }, expectedErrNil: true, @@ -187,7 +217,16 @@ const getPluginRuntimeResponse = `{ const listPluginRuntimeTypedResponse = `{ "request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456", "data": { - "container": ["gvisor"] + "runtimes": [ + { + "name": "gvisor", + "type": "container", + "oci_runtime": "runsc", + "cgroup_parent": "/cpulimit/", + "cpu": 1, + "memory": 10000 + } + ] }, "warnings": null, "auth": null @@ -197,7 +236,32 @@ const listPluginRuntimeTypedResponse = `{ const listPluginRuntimeUntypedResponse = `{ "request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456", "data": { - "container": ["gvisor", "foo", "bar"] + "runtimes": [ + { + "name": "gvisor", + "type": "container", + "oci_runtime": "runsc", + "cgroup_parent": "/cpulimit/", + "cpu": 1, + "memory": 10000 + }, + { + "name": "foo", + "type": "container", + "oci_runtime": "otherociruntime", + "cgroup_parent": "/memorylimit/", + "cpu": 2, + "memory": 20000 + }, + { + "name": "bar", + "type": "container", + "oci_runtime": "otherociruntime", + "cgroup_parent": "/cpulimit/", + "cpu": 3, + "memory": 30000 + } + ] }, "warnings": null, "auth": null diff --git a/vault/logical_system.go b/vault/logical_system.go index 927c940a42bd..8d2c529de77f 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -831,27 +831,40 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l }}, nil } -func (b *SystemBackend) handlePluginRuntimeCatalogUntypedList(ctx context.Context, _ *logical.Request, _ *framework.FieldData) (*logical.Response, error) { - data := make(map[string]interface{}) +func (b *SystemBackend) handlePluginRuntimeCatalogList(ctx context.Context, _ *logical.Request, _ *framework.FieldData) (*logical.Response, error) { + var data []map[string]interface{} for _, runtimeType := range consts.PluginRuntimeTypes { if runtimeType == consts.PluginRuntimeTypeUnsupported { continue } - runtimes, err := b.Core.pluginRuntimeCatalog.List(ctx, runtimeType) + configs, err := b.Core.pluginRuntimeCatalog.List(ctx, runtimeType) if err != nil { return nil, err } - if len(runtimes) > 0 { - sort.Strings(runtimes) - data[runtimeType.String()] = runtimes + + if len(configs) > 0 { + sort.Slice(configs, func(i, j int) bool { + return strings.Compare(configs[i].Name, configs[j].Name) == -1 + }) + for _, conf := range configs { + data = append(data, map[string]interface{}{ + "name": conf.Name, + "type": conf.Type.String(), + "oci_runtime": conf.OCIRuntime, + "cgroup_parent": conf.CgroupParent, + "cpu": conf.CPU, + "memory": conf.Memory, + }) + } } } resp := &logical.Response{ Data: map[string]interface{}{}, } + if len(data) > 0 { - resp.Data["types"] = data + resp.Data["runtimes"] = data } return resp, nil diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index 1114c2994c29..a8fc092c252d 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -2224,14 +2224,14 @@ func (b *SystemBackend) pluginsRuntimesCatalogListPaths() []*framework.Path { Operations: map[logical.Operation]framework.OperationHandler{ logical.ListOperation: &framework.PathOperation{ - Callback: b.handlePluginRuntimeCatalogUntypedList, + Callback: b.handlePluginRuntimeCatalogList, Responses: map[int][]framework.Response{ http.StatusOK: {{ Description: "OK", Fields: map[string]*framework.FieldSchema{ - "types": { - Type: framework.TypeMap, - Description: "Map of plugin runtime types to their plugin runtime names in the catalog", + "runtimes": { + Type: framework.TypeSlice, + Description: "List of all plugin runtimes in the catalog", Required: true, }, }, diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 608f00e2364d..1cc23d5f9dab 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -33,6 +33,7 @@ import ( "github.com/hashicorp/vault/sdk/helper/compressutil" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/jsonutil" + "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" "github.com/hashicorp/vault/sdk/helper/pluginutil" "github.com/hashicorp/vault/sdk/helper/testhelpers/schema" "github.com/hashicorp/vault/sdk/logical" @@ -5888,13 +5889,22 @@ func TestSystemBackend_ReadExperiments(t *testing.T) { func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { b := testSystemBackend(t) + conf := pluginruntimeutil.PluginRuntimeConfig{ + Name: "foo", + Type: consts.PluginRuntimeTypeContainer, + OCIRuntime: "some-oci-runtime", + CgroupParent: "/cpulimit/", + CPU: 1, + Memory: 10000, + } + // Register the plugin runtime - req := logical.TestRequest(t, logical.UpdateOperation, "plugins/runtimes/catalog/container/foo") + req := logical.TestRequest(t, logical.UpdateOperation, fmt.Sprintf("plugins/runtimes/catalog/%s/%s", conf.Type.String(), conf.Name)) req.Data = map[string]interface{}{ - "oci_runtime": "some-oci-runtime", - "cgroup_parent": "/cpulimit/", - "cpu": 1, - "memory": 10000, + "oci_runtime": conf.OCIRuntime, + "cgroup_parent": conf.OCIRuntime, + "cpu": conf.CPU, + "memory": conf.Memory, } resp, err := b.HandleRequest(namespace.RootContext(nil), req) @@ -5928,16 +5938,16 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { true, ) - exp := map[string]interface{}{ - "name": "foo", - "type": "container", - "oci_runtime": "some-oci-runtime", - "cgroup_parent": "/cpulimit/", - "cpu": int64(1), - "memory": int64(10000), + readExp := map[string]interface{}{ + "type": conf.Type.String(), + "name": conf.Name, + "oci_runtime": conf.OCIRuntime, + "cgroup_parent": conf.OCIRuntime, + "cpu": conf.CPU, + "memory": conf.Memory, } - if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + if !reflect.DeepEqual(resp.Data, readExp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, readExp) } // List the plugin runtimes (untyped or all) @@ -5947,11 +5957,11 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { t.Fatalf("err: %v", err) } - exp = map[string]interface{}{ - "types": map[string]interface{}{"container": []string{"foo"}}, + listExp := map[string]interface{}{ + "runtimes": []map[string]interface{}{readExp}, } - if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + if !reflect.DeepEqual(resp.Data, listExp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, listExp) } // Delete the plugin runtime @@ -5989,8 +5999,8 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { t.Fatalf("err: %v", err) } - exp = map[string]interface{}{} - if !reflect.DeepEqual(resp.Data, exp) { - t.Fatalf("got: %#v expect: %#v", resp.Data, exp) + listExp = map[string]interface{}{} + if !reflect.DeepEqual(resp.Data, listExp) { + t.Fatalf("got: %#v expect: %#v", resp.Data, listExp) } } diff --git a/vault/plugin_runtime_catalog.go b/vault/plugin_runtime_catalog.go index 551e7c47b366..9cb0f58869fd 100644 --- a/vault/plugin_runtime_catalog.go +++ b/vault/plugin_runtime_catalog.go @@ -109,11 +109,11 @@ func (c *PluginRuntimeCatalog) Delete(ctx context.Context, name string, prt cons return c.catalogView.Delete(ctx, storageKey) } -func (c *PluginRuntimeCatalog) List(ctx context.Context, prt consts.PluginRuntimeType) ([]string, error) { +func (c *PluginRuntimeCatalog) List(ctx context.Context, prt consts.PluginRuntimeType) ([]*pluginruntimeutil.PluginRuntimeConfig, error) { c.lock.RLock() defer c.lock.RUnlock() - var retList []string + var retList []*pluginruntimeutil.PluginRuntimeConfig keys, err := logical.CollectKeys(ctx, c.catalogView) if err != nil { return nil, err @@ -125,16 +125,16 @@ func (c *PluginRuntimeCatalog) List(ctx context.Context, prt consts.PluginRuntim continue } - runner := new(pluginruntimeutil.PluginRuntimeConfig) - if err := jsonutil.DecodeJSON(entry.Value, runner); err != nil { + conf := new(pluginruntimeutil.PluginRuntimeConfig) + if err := jsonutil.DecodeJSON(entry.Value, conf); err != nil { return nil, fmt.Errorf("failed to decode plugin runtime entry: %w", err) } - if runner.Type != prt { + if conf.Type != prt { continue } - retList = append(retList, runner.Name) + retList = append(retList, conf) } return retList, nil } diff --git a/vault/plugin_runtime_catalog_test.go b/vault/plugin_runtime_catalog_test.go index b576cfd61ef7..50bded62fca4 100644 --- a/vault/plugin_runtime_catalog_test.go +++ b/vault/plugin_runtime_catalog_test.go @@ -57,12 +57,12 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) { t.Fatalf("expected did not match actual, got %#v\n expected %#v\n", runner, expected) } - names, err := core.pluginRuntimeCatalog.List(ctx, expected.Type) + configs, err := core.pluginRuntimeCatalog.List(ctx, expected.Type) if err != nil { t.Fatalf("err: %v", err) } - if len(names) != 1 { - t.Fatalf("expected plugin runtime catalog to have 1 container runtime but got %d", len(names)) + if len(configs) != 1 { + t.Fatalf("expected plugin runtime catalog to have 1 container runtime but got %d", len(configs)) } // Delete plugin runtime @@ -72,11 +72,11 @@ func TestPluginRuntimeCatalog_CRUD(t *testing.T) { } // Assert the plugin runtime catalog is empty - names, err = core.pluginRuntimeCatalog.List(ctx, expected.Type) + configs, err = core.pluginRuntimeCatalog.List(ctx, expected.Type) if err != nil { t.Fatalf("err: %v", err) } - if len(names) != 0 { - t.Fatalf("expected plugin runtime catalog to have 0 container runtimes but got %d", len(names)) + if len(configs) != 0 { + t.Fatalf("expected plugin runtime catalog to have 0 container runtimes but got %d", len(configs)) } } From 75d6d48fb0627113260fe57dba70807096b07279 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 18:07:23 -0700 Subject: [PATCH 12/20] using encoding/json to marshal and unmarshal runtimes in Data instead of mapstructure.Decode --- api/sys_plugins_runtimes.go | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index 0c605f55a3a9..83b8ab5debf2 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -5,11 +5,10 @@ package api import ( "context" + "encoding/json" "errors" "fmt" "net/http" - - "github.com/mitchellh/mapstructure" ) // GetPluginRuntimeInput is used as input to the GetPluginRuntime function. @@ -162,35 +161,25 @@ func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput return nil, fmt.Errorf("data from server response does not contain runtimes") } - runtimesRaw, ok := secret.Data["runtimes"].([]interface{}) - if !ok { - return nil, fmt.Errorf("unable to parse runtimes") + runtimesJSON, err := json.Marshal(secret.Data["runtimes"]) + if err != nil { + return nil, fmt.Errorf("unable to marshal runtimes") } - result := &ListPluginRuntimesResponse{ - Runtimes: []PluginRuntimeDetails{}, - } var runtimes []PluginRuntimeDetails - for _, runtimeRaw := range runtimesRaw { - var runtime PluginRuntimeDetails - if err = mapstructure.Decode(runtimeRaw, &runtime); err != nil { - return nil, err - } - runtimes = append(runtimes, runtime) + if err = json.Unmarshal(runtimesJSON, &runtimes); err != nil { + return nil, fmt.Errorf("unable to parse runtimes") } // return all runtimes in the catalog if i == nil { - result.Runtimes = runtimes - return result, nil + return &ListPluginRuntimesResponse{Runtimes: runtimes}, nil } - switch i.Type { - default: - for _, runtime := range runtimes { - if runtime.Type == i.Type.String() { - result.Runtimes = append(result.Runtimes, runtime) - } + result := &ListPluginRuntimesResponse{Runtimes: []PluginRuntimeDetails{}} + for _, runtime := range runtimes { + if runtime.Type == i.Type.String() { + result.Runtimes = append(result.Runtimes, runtime) } } return result, nil From 9e59d843b4a0ce841e74eff47348fbcc8e6f4744 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Wed, 30 Aug 2023 18:34:55 -0700 Subject: [PATCH 13/20] change data from type []map[string]interface{} to []map[string]any --- vault/logical_system.go | 4 ++-- vault/logical_system_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vault/logical_system.go b/vault/logical_system.go index 8d2c529de77f..cfef50b7c53d 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -832,7 +832,7 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l } func (b *SystemBackend) handlePluginRuntimeCatalogList(ctx context.Context, _ *logical.Request, _ *framework.FieldData) (*logical.Response, error) { - var data []map[string]interface{} + var data []map[string]any for _, runtimeType := range consts.PluginRuntimeTypes { if runtimeType == consts.PluginRuntimeTypeUnsupported { continue @@ -847,7 +847,7 @@ func (b *SystemBackend) handlePluginRuntimeCatalogList(ctx context.Context, _ *l return strings.Compare(configs[i].Name, configs[j].Name) == -1 }) for _, conf := range configs { - data = append(data, map[string]interface{}{ + data = append(data, map[string]any{ "name": conf.Name, "type": conf.Type.String(), "oci_runtime": conf.OCIRuntime, diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 1cc23d5f9dab..a235c2e5830e 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -5938,7 +5938,7 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { true, ) - readExp := map[string]interface{}{ + readExp := map[string]any{ "type": conf.Type.String(), "name": conf.Name, "oci_runtime": conf.OCIRuntime, @@ -5958,7 +5958,7 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { } listExp := map[string]interface{}{ - "runtimes": []map[string]interface{}{readExp}, + "runtimes": []map[string]any{readExp}, } if !reflect.DeepEqual(resp.Data, listExp) { t.Fatalf("got: %#v expect: %#v", resp.Data, listExp) From 48bfbc05e4696e460996d328451f0f4ff5e4f9ca Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 08:31:59 -0700 Subject: [PATCH 14/20] Update api/sys_plugins_runtimes.go Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com> --- api/sys_plugins_runtimes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index 83b8ab5debf2..49f080794ca4 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -187,5 +187,5 @@ func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput // pluginRuntimeCatalogPathByType is a helper to construct the proper API path by plugin type func pluginRuntimeCatalogPathByType(runtimeType PluginRuntimeType, name string) string { - return fmt.Sprintf("/v1/sys/plugins/catalog/%s/%s", runtimeType, name) + return fmt.Sprintf("/v1/sys/plugins/runtimes/catalog/%s/%s", runtimeType, name) } From b0308039cfbf5b448f3bb175db086ab6a69166c0 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 08:33:36 -0700 Subject: [PATCH 15/20] Update vault/logical_system.go Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com> --- vault/logical_system.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vault/logical_system.go b/vault/logical_system.go index cfef50b7c53d..898b63f4787a 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -751,11 +751,11 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ cgroupParent := d.Get("cgroup_parent").(string) cpu := d.Get("cpu").(int64) if cpu < 0 { - return logical.ErrorResponse("runtime cpu must be greater than 0"), nil + return logical.ErrorResponse("runtime cpu cannot be negative"), nil } memory := d.Get("memory").(int64) if memory < 0 { - return logical.ErrorResponse("runtime memory in bytes must be greater than 0"), nil + return logical.ErrorResponse("runtime memory in bytes cannot be negative"), nil } if err = b.Core.pluginRuntimeCatalog.Set(ctx, &pluginruntimeutil.PluginRuntimeConfig{ From 5a1898815ed0c9eade10f495fb3a898eaab81a7e Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 08:52:36 -0700 Subject: [PATCH 16/20] add mapstructure tags to PluginRuntimeDetails --- api/sys_plugins_runtimes.go | 38 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index 49f080794ca4..c7037b2e760e 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -5,10 +5,11 @@ package api import ( "context" - "encoding/json" "errors" "fmt" "net/http" + + "github.com/mitchellh/mapstructure" ) // GetPluginRuntimeInput is used as input to the GetPluginRuntime function. @@ -111,12 +112,12 @@ func (c *Sys) DeregisterPluginRuntime(ctx context.Context, i *DeregisterPluginRu } type PluginRuntimeDetails struct { - Type string `json:"type"` - Name string `json:"name"` - OCIRuntime string `json:"oci_runtime"` - CgroupParent string `json:"cgroup_parent"` - CPU int64 `json:"cpu"` - Memory int64 `json:"memory"` + Type string `json:"type" mapstructure:"type"` + Name string `json:"name" mapstructure:"name"` + OCIRuntime string `json:"oci_runtime" mapstructure:"oci_runtime"` + CgroupParent string `json:"cgroup_parent" mapstructure:"cgroup_parent"` + CPU int64 `json:"cpu" mapstructure:"cpu"` + Memory int64 `json:"memory" mapstructure:"memory"` } // ListPluginRuntimesInput is used as input to the ListPluginRuntimes function. @@ -133,12 +134,12 @@ type ListPluginRuntimesResponse struct { // ListPluginRuntimes lists all plugin runtimes in the catalog and returns their names as a // list of strings. -func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { +func (c *Sys) ListPluginRuntimes(ctx context.Context, input *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) { ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) defer cancelFunc() - if i != nil && i.Type == PluginRuntimeTypeUnsupported { - return nil, fmt.Errorf("%q is not a supported runtime type", i.Type.String()) + if input != nil && input.Type == PluginRuntimeTypeUnsupported { + return nil, fmt.Errorf("%q is not a supported runtime type", input.Type.String()) } resp, err := c.c.rawRequestWithContext(ctx, c.c.NewRequest(http.MethodGet, "/v1/sys/plugins/runtimes/catalog")) @@ -161,24 +162,21 @@ func (c *Sys) ListPluginRuntimes(ctx context.Context, i *ListPluginRuntimesInput return nil, fmt.Errorf("data from server response does not contain runtimes") } - runtimesJSON, err := json.Marshal(secret.Data["runtimes"]) - if err != nil { - return nil, fmt.Errorf("unable to marshal runtimes") - } - var runtimes []PluginRuntimeDetails - if err = json.Unmarshal(runtimesJSON, &runtimes); err != nil { - return nil, fmt.Errorf("unable to parse runtimes") + if err = mapstructure.Decode(secret.Data["runtimes"], &runtimes); err != nil { + return nil, err } // return all runtimes in the catalog - if i == nil { + if input == nil { return &ListPluginRuntimesResponse{Runtimes: runtimes}, nil } - result := &ListPluginRuntimesResponse{Runtimes: []PluginRuntimeDetails{}} + result := &ListPluginRuntimesResponse{ + Runtimes: []PluginRuntimeDetails{}, + } for _, runtime := range runtimes { - if runtime.Type == i.Type.String() { + if runtime.Type == input.Type.String() { result.Runtimes = append(result.Runtimes, runtime) } } From 257d6fe18bc3b5d8e94d0b3092375a119e0ecf25 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 10:03:53 -0700 Subject: [PATCH 17/20] rename fields cpu and memory to cpu_nanos and memory_bytes --- api/sys_plugins_runtimes.go | 8 ++++---- api/sys_plugins_runtimes_test.go | 20 ++++++++++---------- sdk/helper/pluginruntimeutil/config.go | 6 ------ vault/logical_system.go | 22 +++++++++++----------- vault/logical_system_paths.go | 20 ++++++++------------ vault/logical_system_test.go | 8 ++++---- 6 files changed, 37 insertions(+), 47 deletions(-) diff --git a/api/sys_plugins_runtimes.go b/api/sys_plugins_runtimes.go index c7037b2e760e..c3380a85d1bf 100644 --- a/api/sys_plugins_runtimes.go +++ b/api/sys_plugins_runtimes.go @@ -26,8 +26,8 @@ type GetPluginRuntimeResponse struct { Name string `json:"name"` OCIRuntime string `json:"oci_runtime"` CgroupParent string `json:"cgroup_parent"` - CPU int64 `json:"cpu"` - Memory int64 `json:"memory"` + CPU int64 `json:"cpu_nanos"` + Memory int64 `json:"memory_bytes"` } // GetPluginRuntime retrieves information about the plugin. @@ -116,8 +116,8 @@ type PluginRuntimeDetails struct { Name string `json:"name" mapstructure:"name"` OCIRuntime string `json:"oci_runtime" mapstructure:"oci_runtime"` CgroupParent string `json:"cgroup_parent" mapstructure:"cgroup_parent"` - CPU int64 `json:"cpu" mapstructure:"cpu"` - Memory int64 `json:"memory" mapstructure:"memory"` + CPU int64 `json:"cpu_nanos" mapstructure:"cpu_nanos"` + Memory int64 `json:"memory_bytes" mapstructure:"memory_bytes"` } // ListPluginRuntimesInput is used as input to the ListPluginRuntimes function. diff --git a/api/sys_plugins_runtimes_test.go b/api/sys_plugins_runtimes_test.go index 58a61eb869f3..6c3486a31a00 100644 --- a/api/sys_plugins_runtimes_test.go +++ b/api/sys_plugins_runtimes_test.go @@ -207,8 +207,8 @@ const getPluginRuntimeResponse = `{ "type": "container", "oci_runtime": "runsc", "cgroup_parent": "/cpulimit/", - "cpu": 1, - "memory": 10000 + "cpu_nanos": 1, + "memory_bytes": 10000 }, "warnings": null, "auth": null @@ -223,8 +223,8 @@ const listPluginRuntimeTypedResponse = `{ "type": "container", "oci_runtime": "runsc", "cgroup_parent": "/cpulimit/", - "cpu": 1, - "memory": 10000 + "cpu_nanos": 1, + "memory_bytes": 10000 } ] }, @@ -242,24 +242,24 @@ const listPluginRuntimeUntypedResponse = `{ "type": "container", "oci_runtime": "runsc", "cgroup_parent": "/cpulimit/", - "cpu": 1, - "memory": 10000 + "cpu_nanos": 1, + "memory_bytes": 10000 }, { "name": "foo", "type": "container", "oci_runtime": "otherociruntime", "cgroup_parent": "/memorylimit/", - "cpu": 2, - "memory": 20000 + "cpu_nanos": 2, + "memory_bytes": 20000 }, { "name": "bar", "type": "container", "oci_runtime": "otherociruntime", "cgroup_parent": "/cpulimit/", - "cpu": 3, - "memory": 30000 + "cpu_nanos": 3, + "memory_bytes": 30000 } ] }, diff --git a/sdk/helper/pluginruntimeutil/config.go b/sdk/helper/pluginruntimeutil/config.go index b72fa1ead785..4a361ddfc7b5 100644 --- a/sdk/helper/pluginruntimeutil/config.go +++ b/sdk/helper/pluginruntimeutil/config.go @@ -5,12 +5,6 @@ package pluginruntimeutil import "github.com/hashicorp/vault/sdk/helper/consts" -const ( - DefaultOCIRuntime = "runsc" - DefaultCPU = 1 - DefaultMemory = 100000000 -) - // PluginRuntimeConfig defines the metadata needed to run a plugin runtime type PluginRuntimeConfig struct { Name string `json:"name" structs:"name"` diff --git a/vault/logical_system.go b/vault/logical_system.go index 898b63f4787a..e7d56ea6f6a5 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -749,11 +749,11 @@ func (b *SystemBackend) handlePluginRuntimeCatalogUpdate(ctx context.Context, _ case consts.PluginRuntimeTypeContainer: ociRuntime := d.Get("oci_runtime").(string) cgroupParent := d.Get("cgroup_parent").(string) - cpu := d.Get("cpu").(int64) + cpu := d.Get("cpu_nanos").(int64) if cpu < 0 { - return logical.ErrorResponse("runtime cpu cannot be negative"), nil + return logical.ErrorResponse("runtime cpu in nanos cannot be negative"), nil } - memory := d.Get("memory").(int64) + memory := d.Get("memory_bytes").(int64) if memory < 0 { return logical.ErrorResponse("runtime memory in bytes cannot be negative"), nil } @@ -826,8 +826,8 @@ func (b *SystemBackend) handlePluginRuntimeCatalogRead(ctx context.Context, _ *l "type": conf.Type.String(), "oci_runtime": conf.OCIRuntime, "cgroup_parent": conf.CgroupParent, - "cpu": conf.CPU, - "memory": conf.Memory, + "cpu_nanos": conf.CPU, + "memory_bytes": conf.Memory, }}, nil } @@ -852,8 +852,8 @@ func (b *SystemBackend) handlePluginRuntimeCatalogList(ctx context.Context, _ *l "type": conf.Type.String(), "oci_runtime": conf.OCIRuntime, "cgroup_parent": conf.CgroupParent, - "cpu": conf.CPU, - "memory": conf.Memory, + "cpu_nanos": conf.CPU, + "memory_bytes": conf.Memory, }) } } @@ -6129,12 +6129,12 @@ This path responds to the following HTTP methods. "Optional parent cgroup for the container", "", }, - "plugin-runtime-catalog_cpu": { - "The limit of runtime CPU (default 0.1)", + "plugin-runtime-catalog_cpu-nanos": { + "The limit of runtime CPU in nanos", "", }, - "plugin-runtime-catalog_memory": { - "The limit of runtime memory in bytes (default 100000000)", + "plugin-runtime-catalog_memory-bytes": { + "The limit of runtime memory in bytes", "", }, "leases": { diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index a8fc092c252d..1d143493b944 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/helper/pluginruntimeutil" "github.com/hashicorp/vault/sdk/logical" ) @@ -2114,21 +2113,18 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { "oci_runtime": { Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_oci-runtime"][0]), - Default: pluginruntimeutil.DefaultOCIRuntime, }, "cgroup_parent": { Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cgroup-parent"][0]), }, - "cpu": { + "cpu_nanos": { Type: framework.TypeInt64, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), - Default: pluginruntimeutil.DefaultCPU, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu-nanos"][0]), }, - "memory": { + "memory_bytes": { Type: framework.TypeInt64, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory"][0]), - Default: pluginruntimeutil.DefaultMemory, + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory-bytes"][0]), }, }, @@ -2189,14 +2185,14 @@ func (b *SystemBackend) pluginsRuntimesCatalogCRUDPath() *framework.Path { Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cgroup-parent"][0]), Required: true, }, - "cpu": { + "cpu_nanos": { Type: framework.TypeInt64, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu"][0]), + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_cpu-nanos"][0]), Required: true, }, - "memory": { + "memory_bytes": { Type: framework.TypeInt64, - Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory"][0]), + Description: strings.TrimSpace(sysHelp["plugin-runtime-catalog_memory-bytes"][0]), Required: true, }, }, diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index a235c2e5830e..3cbabf642d13 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -5903,8 +5903,8 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { req.Data = map[string]interface{}{ "oci_runtime": conf.OCIRuntime, "cgroup_parent": conf.OCIRuntime, - "cpu": conf.CPU, - "memory": conf.Memory, + "cpu_nanos": conf.CPU, + "memory_bytes": conf.Memory, } resp, err := b.HandleRequest(namespace.RootContext(nil), req) @@ -5943,8 +5943,8 @@ func TestSystemBackend_pluginRuntimeCRUD(t *testing.T) { "name": conf.Name, "oci_runtime": conf.OCIRuntime, "cgroup_parent": conf.OCIRuntime, - "cpu": conf.CPU, - "memory": conf.Memory, + "cpu_nanos": conf.CPU, + "memory_bytes": conf.Memory, } if !reflect.DeepEqual(resp.Data, readExp) { t.Fatalf("got: %#v expect: %#v", resp.Data, readExp) From 47353fce1b22e67b98fb065285f04f74b2188ddd Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 10:47:37 -0700 Subject: [PATCH 18/20] add /sys/plugins/runtimes/catalog/{type}{name} to sudo paths --- api/sudo_paths.go | 27 ++++++++++++++------------- api/sudo_paths_test.go | 5 +++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/api/sudo_paths.go b/api/sudo_paths.go index fb7113a0f3b3..020cc09ac0df 100644 --- a/api/sudo_paths.go +++ b/api/sudo_paths.go @@ -32,19 +32,20 @@ var sudoPaths = map[string]*regexp.Regexp{ // This entry is a bit wrong... sys/leases/lookup does NOT require sudo. But sys/leases/lookup/ with a trailing // slash DOES require sudo. But the part of the Vault CLI that uses this logic doesn't pass operation-appropriate // trailing slashes, it always strips them off, so we end up giving the wrong answer for one of these. - "/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup(?:/.+)?$`), - "/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`), - "/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`), - "/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`), - "/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`), - "/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`), - "/sys/raw/{path}": regexp.MustCompile(`^/sys/raw(?:/.+)?$`), - "/sys/remount": regexp.MustCompile(`^/sys/remount$`), - "/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`), - "/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`), - "/sys/rotate": regexp.MustCompile(`^/sys/rotate$`), - "/sys/seal": regexp.MustCompile(`^/sys/seal$`), - "/sys/step-down": regexp.MustCompile(`^/sys/step-down$`), + "/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup(?:/.+)?$`), + "/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`), + "/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`), + "/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`), + "/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`), + "/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`), + "/sys/plugins/runtimes/catalog/{type}{name}": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/[\w-]+/[^/]+$`), + "/sys/raw/{path}": regexp.MustCompile(`^/sys/raw(?:/.+)?$`), + "/sys/remount": regexp.MustCompile(`^/sys/remount$`), + "/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`), + "/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`), + "/sys/rotate": regexp.MustCompile(`^/sys/rotate$`), + "/sys/seal": regexp.MustCompile(`^/sys/seal$`), + "/sys/step-down": regexp.MustCompile(`^/sys/step-down$`), // enterprise-only paths "/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`), diff --git a/api/sudo_paths_test.go b/api/sudo_paths_test.go index 2e97d44cb5b9..b23af7067fc9 100644 --- a/api/sudo_paths_test.go +++ b/api/sudo_paths_test.go @@ -55,6 +55,11 @@ func TestIsSudoPath(t *testing.T) { "/sys/plugins/catalog/some-type/some/name/with/slashes", false, }, + // Testing: sys/plugins/runtimes/catalog/{type}/{name} + { + "/sys/plugins/runtimes/catalog/some-type/some-name", + true, + }, // Testing: auth/token/accessors (an example of a sudo path that only accepts list operations) // It is matched as sudo without the trailing slash... { From bbb7c88b65abc40402f04e6c3b7f18bb0b851e32 Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 12:43:14 -0700 Subject: [PATCH 19/20] add /sys/plugins/runtimes/catalog to sudo paths --- api/sudo_paths.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/api/sudo_paths.go b/api/sudo_paths.go index 020cc09ac0df..faffdfb0e853 100644 --- a/api/sudo_paths.go +++ b/api/sudo_paths.go @@ -32,20 +32,21 @@ var sudoPaths = map[string]*regexp.Regexp{ // This entry is a bit wrong... sys/leases/lookup does NOT require sudo. But sys/leases/lookup/ with a trailing // slash DOES require sudo. But the part of the Vault CLI that uses this logic doesn't pass operation-appropriate // trailing slashes, it always strips them off, so we end up giving the wrong answer for one of these. - "/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup(?:/.+)?$`), - "/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`), - "/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`), - "/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`), - "/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`), - "/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`), - "/sys/plugins/runtimes/catalog/{type}{name}": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/[\w-]+/[^/]+$`), - "/sys/raw/{path}": regexp.MustCompile(`^/sys/raw(?:/.+)?$`), - "/sys/remount": regexp.MustCompile(`^/sys/remount$`), - "/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`), - "/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`), - "/sys/rotate": regexp.MustCompile(`^/sys/rotate$`), - "/sys/seal": regexp.MustCompile(`^/sys/seal$`), - "/sys/step-down": regexp.MustCompile(`^/sys/step-down$`), + "/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup(?:/.+)?$`), + "/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`), + "/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`), + "/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`), + "/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`), + "/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`), + "/sys/plugins/runtimes/catalog": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/?$`), + "/sys/plugins/runtimes/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/[\w-]+/[\w-]+$`), + "/sys/raw/{path}": regexp.MustCompile(`^/sys/raw(?:/.+)?$`), + "/sys/remount": regexp.MustCompile(`^/sys/remount$`), + "/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`), + "/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`), + "/sys/rotate": regexp.MustCompile(`^/sys/rotate$`), + "/sys/seal": regexp.MustCompile(`^/sys/seal$`), + "/sys/step-down": regexp.MustCompile(`^/sys/step-down$`), // enterprise-only paths "/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`), From c5185d385167b78ccd93ba5e4915db3566620bad Mon Sep 17 00:00:00 2001 From: Thy Ton <maithytonn@gmail.com> Date: Thu, 31 Aug 2023 12:49:10 -0700 Subject: [PATCH 20/20] revert the regex change in /sys/plugins/runtimes/catalog/{type}/{name} --- api/sudo_paths.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/sudo_paths.go b/api/sudo_paths.go index faffdfb0e853..24beb4bb1f2a 100644 --- a/api/sudo_paths.go +++ b/api/sudo_paths.go @@ -39,7 +39,7 @@ var sudoPaths = map[string]*regexp.Regexp{ "/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`), "/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`), "/sys/plugins/runtimes/catalog": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/?$`), - "/sys/plugins/runtimes/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/[\w-]+/[\w-]+$`), + "/sys/plugins/runtimes/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/runtimes/catalog/[\w-]+/[^/]+$`), "/sys/raw/{path}": regexp.MustCompile(`^/sys/raw(?:/.+)?$`), "/sys/remount": regexp.MustCompile(`^/sys/remount$`), "/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`),