-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
add plugin runtime API #22469
Conversation
Build Results: |
CI Results: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! LMK any thoughts on the API path comments. This is a good sized unit of work to land so definitely save the CLI changes for another PR. Feel free to ping me when the TODOs are tidied up (or earlier if you prefer) and I can take another more detailed pass.
Co-authored-by: Tom Proctor <[email protected]>
Co-authored-by: Tom Proctor <[email protected]>
…/vault into VAULT-18181-plugin-runtime-api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly nits and follow ups. The only substantive comments are to replace float with integer types throughout (unless I'm missing something) and deleting some APIs that relate to backwards compatibility baggage from the plugin catalog. Otherwise, LGTM!
vault/plugin_runtime_catalog.go
Outdated
return ErrPluginRuntimeNotFound | ||
} | ||
|
||
// TODO what happens to the running existing container with(out) running plugins. Need to shut it down before setting it? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation doesn't belong in this PR, but I think we should handle this case higher up in the call stack - i.e. in the delete API handler function.
…context.Context parameter
… of mapstructure.Decode
api/sys_plugins_runtimes.go
Outdated
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") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a bit of struggle parsing the Data back to runtimes after changing to store the returned runtimes in Data as []map[string]any
Lines 835 to 871 in 9e59d84
var data []map[string]any | |
for _, runtimeType := range consts.PluginRuntimeTypes { | |
if runtimeType == consts.PluginRuntimeTypeUnsupported { | |
continue | |
} | |
configs, err := b.Core.pluginRuntimeCatalog.List(ctx, runtimeType) | |
if err != nil { | |
return nil, err | |
} | |
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]any{ | |
"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["runtimes"] = data | |
} | |
return resp, nil | |
} |
{
"request_id": "e93d3f93-8e4f-8443-a803-f1c97c123456",
"data": {
"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
}
]
},
"warnings": null,
"auth": null
}
I attempted use mapstructure.Decode
different ways to parse Response.Data["runtimes"]
but TestList*
sys_plugins_runtimes_test.go couldn't pass asOCIRuntime:"", CgroupParent:""
weren't parsed correctly.
=== RUN TestListPluginRuntimeTyped/container
sys_plugins_runtimes_test.go:135: expected: &api.ListPluginRuntimesResponse{Runtimes:[]api.PluginRuntimeDetails{api.PluginRuntimeDetails{Type:"container", Name:"gvisor", OCIRuntime:"runsc", CgroupParent:"/cpulimit/", CPU:1, Memory:10000}}}
got: &api.ListPluginRuntimesResponse{Runtimes:[]api.PluginRuntimeDetails{api.PluginRuntimeDetails{Type:"container", Name:"gvisor", OCIRuntime:"", CgroupParent:"", CPU:1, Memory:10000}}}
=== RUN TestListPluginRuntimeTyped/unsupported
--- FAIL: TestListPluginRuntimeTyped (0.01s)
=== RUN TestListPluginRuntimeUntyped/#00
sys_plugins_runtimes_test.go:197: expected: &api.ListPluginRuntimesResponse{Runtimes:[]api.PluginRuntimeDetails{api.PluginRuntimeDetails{Type:"container", Name:"gvisor", OCIRuntime:"runsc", CgroupParent:"/cpulimit/", CPU:1, Memory:10000}, api.PluginRuntimeDetails{Type:"container", Name:"foo", OCIRuntime:"otherociruntime", CgroupParent:"/memorylimit/", CPU:2, Memory:20000}, api.PluginRuntimeDetails{Type:"container", Name:"bar", OCIRuntime:"otherociruntime", CgroupParent:"/cpulimit/", CPU:3, Memory:30000}}}
got: &api.ListPluginRuntimesResponse{Runtimes:[]api.PluginRuntimeDetails{api.PluginRuntimeDetails{Type:"container", Name:"gvisor", OCIRuntime:"", CgroupParent:"", CPU:1, Memory:10000}, api.PluginRuntimeDetails{Type:"container", Name:"foo", OCIRuntime:"", CgroupParent:"", CPU:2, Memory:20000}, api.PluginRuntimeDetails{Type:"container", Name:"bar", OCIRuntime:"", CgroupParent:"", CPU:3, Memory:30000}}}
--- FAIL: TestListPluginRuntimeUntyped (0.01s)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After mapstructure.Decode
attemps, I resorted to use json.Marshal
and json.Unmarshal
to put Data["runtimes"] in JSON bytes and let the library do the work, and the tests pass now. Admittedly, I feel this is a bit weird and hacky way. I'm open to any thoughts and tips that you have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "bug" here was that mapstructure
didn't correctly guess the translation between field name and JSON key. You can give it explicit instructions in 2 ways: either create a mapstructure.DecoderConfig
and set the TagName
to "json"
so that it uses the json tags instead of its default "mapstructure". Or you can set the mapstructure
keys in the struct like so:
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" mapstructure:"cpu"`
Memory int64 `json:"memory" mapstructure:"memory"`
}
If you go with the latter method, the tests pass with this as the decoding step, which is probably my personal preference because the "interesting" code is more straightforward:
if err = mapstructure.Decode(secret.Data["runtimes"], &runtimes); err != nil {
return nil, fmt.Errorf("unable to parse runtimes")
}
Setting DecoderConfig
looks like this:
decoderConfig := mapstructure.DecoderConfig{
TagName: "json",
Result: &runtimes,
}
decoder, err := mapstructure.NewDecoder(&decoderConfig)
if err != nil {
return nil, err
}
if err = decoder.Decode(secret.Data["runtimes"]); err != nil {
return nil, fmt.Errorf("unable to parse runtimes")
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! One potential typo and just nits otherwise
api/sys_plugins_runtimes.go
Outdated
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") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "bug" here was that mapstructure
didn't correctly guess the translation between field name and JSON key. You can give it explicit instructions in 2 ways: either create a mapstructure.DecoderConfig
and set the TagName
to "json"
so that it uses the json tags instead of its default "mapstructure". Or you can set the mapstructure
keys in the struct like so:
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" mapstructure:"cpu"`
Memory int64 `json:"memory" mapstructure:"memory"`
}
If you go with the latter method, the tests pass with this as the decoding step, which is probably my personal preference because the "interesting" code is more straightforward:
if err = mapstructure.Decode(secret.Data["runtimes"], &runtimes); err != nil {
return nil, fmt.Errorf("unable to parse runtimes")
}
Setting DecoderConfig
looks like this:
decoderConfig := mapstructure.DecoderConfig{
TagName: "json",
Result: &runtimes,
}
decoder, err := mapstructure.NewDecoder(&decoderConfig)
if err != nil {
return nil, err
}
if err = decoder.Decode(secret.Data["runtimes"]); err != nil {
return nil, fmt.Errorf("unable to parse runtimes")
}
Co-authored-by: Tom Proctor <[email protected]>
Co-authored-by: Tom Proctor <[email protected]>
Implement plugin runtime CRUD API