Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

add plugin runtime API #22469

Merged
merged 24 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a67d58c
add plugin runtime API
thyton Aug 21, 2023
2cfb2a6
Update sdk/helper/pluginruntimeutil/runner.go
thyton Aug 23, 2023
9c360b6
Update sdk/helper/pluginruntimeutil/runner.go
thyton Aug 23, 2023
d3891d1
Merge branch 'VAULT-18181-plugin-runtime-api' of github.com:hashicorp…
thyton Aug 29, 2023
bb32254
change runner.go to config.go
thyton Aug 29, 2023
5107640
wip plugin runtime CRUD test
thyton Aug 29, 2023
b10e679
Merge branch 'main' into VAULT-18181-plugin-runtime-api
thyton Aug 29, 2023
d2b8903
fix plugin runtime CRUD test failure
thyton Aug 30, 2023
10ea40d
Merge branch 'main' into VAULT-18181-plugin-runtime-api
thyton Aug 30, 2023
0769af5
Merge branch 'VAULT-18181-plugin-runtime-api' of github.com:hashicorp…
thyton Aug 30, 2023
8352ecd
add copyright headers
thyton Aug 30, 2023
49a26e0
change cpu to int64 type and refactor PluginRuntimeCatalog.Set()
thyton Aug 30, 2023
66201a1
faithfully represent the data passed to us in storage
thyton Aug 30, 2023
d06ef06
remove ListPluginRuntimesWithContext and use ListPluginRuntimes with …
thyton Aug 30, 2023
8ebbdfb
list API returns a slice of plugin runtimes in response data
thyton Aug 31, 2023
75d6d48
using encoding/json to marshal and unmarshal runtimes in Data instead…
thyton Aug 31, 2023
9e59d84
change data from type []map[string]interface{} to []map[string]any
thyton Aug 31, 2023
48bfbc0
Update api/sys_plugins_runtimes.go
thyton Aug 31, 2023
b030803
Update vault/logical_system.go
thyton Aug 31, 2023
5a18988
add mapstructure tags to PluginRuntimeDetails
thyton Aug 31, 2023
257d6fe
rename fields cpu and memory to cpu_nanos and memory_bytes
thyton Aug 31, 2023
47353fc
add /sys/plugins/runtimes/catalog/{type}{name} to sudo paths
thyton Aug 31, 2023
bbb7c88
add /sys/plugins/runtimes/catalog to sudo paths
thyton Aug 31, 2023
c5185d3
revert the regex change in /sys/plugins/runtimes/catalog/{type}/{name}
thyton Aug 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions api/plugin_runtime_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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{
PluginRuntimeTypeUnsupported,
PluginRuntimeTypeContainer,
}

type PluginRuntimeType uint32

// This is a list of PluginRuntimeTypes used by Vault.
const (
PluginRuntimeTypeUnsupported PluginRuntimeType = iota
PluginRuntimeTypeContainer
)

func (r PluginRuntimeType) String() string {
switch r {
case PluginRuntimeTypeContainer:
return "container"
default:
return "unsupported"
}
}

func ParsePluginRuntimeType(PluginRuntimeType string) (PluginRuntimeType, error) {
switch PluginRuntimeType {
case "container":
return PluginRuntimeTypeContainer, nil
default:
return PluginRuntimeTypeUnsupported, fmt.Errorf("%q is not a supported plugin runtime type", PluginRuntimeType)
}
}
28 changes: 15 additions & 13 deletions api/sudo_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +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/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-]+/[^/]+$`),
"/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$`),
Expand Down
5 changes: 5 additions & 0 deletions api/sudo_paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...
{
Expand Down
189 changes: 189 additions & 0 deletions api/sys_plugins_runtimes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package api

import (
"context"
"errors"
"fmt"
"net/http"

"github.com/mitchellh/mapstructure"
)

// 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"`
CgroupParent string `json:"cgroup_parent"`
CPU int64 `json:"cpu_nanos"`
Memory int64 `json:"memory_bytes"`
}

// 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()

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"`
CgroupParent string `json:"cgroup_parent,omitempty"`
CPU int64 `json:"cpu,omitempty"`
Memory int64 `json:"memory,omitempty"`
}

// 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()

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 removes the plugin with the given name from the plugin
// catalog.
func (c *Sys) DeregisterPluginRuntime(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
}

type PluginRuntimeDetails struct {
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_nanos" mapstructure:"cpu_nanos"`
Memory int64 `json:"memory_bytes" mapstructure:"memory_bytes"`
}

// 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, input *ListPluginRuntimesInput) (*ListPluginRuntimesResponse, error) {
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
defer cancelFunc()

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"))
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")
}
if _, ok := secret.Data["runtimes"]; !ok {
return nil, fmt.Errorf("data from server response does not contain runtimes")
}

var runtimes []PluginRuntimeDetails
if err = mapstructure.Decode(secret.Data["runtimes"], &runtimes); err != nil {
return nil, err
}

// return all runtimes in the catalog
if input == nil {
return &ListPluginRuntimesResponse{Runtimes: runtimes}, nil
}

result := &ListPluginRuntimesResponse{
Runtimes: []PluginRuntimeDetails{},
}
for _, runtime := range runtimes {
if runtime.Type == input.Type.String() {
result.Runtimes = append(result.Runtimes, runtime)
}
}
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/runtimes/catalog/%s/%s", runtimeType, name)
}
Loading