NRI: add daemon.json/command line options

Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
Rob Murray
2025-12-01 09:24:41 +00:00
parent 06eadccf2f
commit a230544000
4 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
package opts
import (
"bytes"
"encoding/csv"
"encoding/json"
"fmt"
"strconv"
"strings"
)
type NRIOpts struct {
Enable bool `json:"enable,omitempty"`
PluginPath string `json:"plugin-path,omitempty"`
PluginConfigPath string `json:"plugin-config-path,omitempty"`
SocketPath string `json:"socket-path,omitempty"`
}
func (c *NRIOpts) UnmarshalJSON(raw []byte) error {
dec := json.NewDecoder(bytes.NewReader(raw))
dec.DisallowUnknownFields()
type nc *NRIOpts // prevent recursion
if err := dec.Decode(nc(c)); err != nil {
return err
}
return nil
}
// NamedNRIOpts is a NamedOption and flags.Value for NRI configuration parsing.
type NamedNRIOpts struct {
Val *NRIOpts
}
func NewNamedNRIOptsRef(val *NRIOpts) *NamedNRIOpts {
return &NamedNRIOpts{Val: val}
}
func (p *NamedNRIOpts) Set(value string) error {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return err
}
for _, field := range fields {
key, val, _ := strings.Cut(field, "=")
switch key {
case "enable":
// Assume "enable=true" if no value given, so that "--nri enable" works.
if val == "" {
val = "true"
}
en, err := strconv.ParseBool(val)
if err != nil {
return fmt.Errorf("invalid value for NRI enable %q: %w", val, err)
}
p.Val.Enable = en
case "plugin-path":
p.Val.PluginPath = val
case "plugin-config-path":
p.Val.PluginConfigPath = val
case "socket-path":
p.Val.SocketPath = val
default:
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
}
}
return nil
}
// Type returns the type of this option
func (p *NamedNRIOpts) Type() string {
return "nri-opts"
}
// String returns a string repr of this option
func (p *NamedNRIOpts) String() string {
vals := []string{fmt.Sprintf("enable=%v", p.Val.Enable)}
if p.Val.PluginPath != "" {
vals = append(vals, "plugin-path="+p.Val.PluginPath)
}
if p.Val.PluginConfigPath != "" {
vals = append(vals, "plugin-config-path="+p.Val.PluginConfigPath)
}
if p.Val.SocketPath != "" {
vals = append(vals, "socket-path="+p.Val.SocketPath)
}
return strings.Join(vals, ",")
}
// Name returns the flag name of this option
func (p *NamedNRIOpts) Name() string {
return "nri-opts"
}

View File

@@ -0,0 +1,117 @@
package opts
import (
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestNRIOptsJSON(t *testing.T) {
tests := []struct {
name string
json string
expErr string
expNRI NRIOpts
}{
{
name: "no config",
expNRI: NRIOpts{},
},
{
name: "json enable",
json: `{"enable": true}`,
expNRI: NRIOpts{
Enable: true,
},
},
{
name: "json enable with paths",
json: `{"enable": true, "plugin-path": "/foo", "plugin-config-path": "/bar", "socket-path": "/baz"}`,
expNRI: NRIOpts{
Enable: true,
PluginPath: "/foo",
PluginConfigPath: "/bar",
SocketPath: "/baz",
},
},
{
name: "json unknown field",
json: `{"foo": "bar"}`,
expErr: "unknown field \"foo\"",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var nc NRIOpts
if tc.json != "" {
err := nc.UnmarshalJSON([]byte(tc.json))
if tc.expErr != "" {
assert.Check(t, is.ErrorContains(err, tc.expErr))
return
}
assert.Check(t, err)
}
assert.Check(t, is.Equal(nc, tc.expNRI))
})
}
}
func TestNRIOptsCmd(t *testing.T) {
tests := []struct {
name string
cmd string
expErr string
expNRI NRIOpts
expString string
}{
{
name: "cmd enable",
cmd: "enable=true",
expNRI: NRIOpts{
Enable: true,
},
expString: "enable=true",
},
{
name: "cmd enable with paths",
cmd: "enable=true,plugin-path=/foo,plugin-config-path=/bar,socket-path=/baz",
expNRI: NRIOpts{
Enable: true,
PluginPath: "/foo",
PluginConfigPath: "/bar",
SocketPath: "/baz",
},
expString: "enable=true,plugin-path=/foo,plugin-config-path=/bar,socket-path=/baz",
},
{
// "--nri-opts enable"
name: "cmd enable no value",
cmd: `enable`,
expNRI: NRIOpts{
Enable: true,
},
expString: "enable=true",
},
{
name: "cmd unknown field",
cmd: `foo=bar`,
expErr: "unexpected key 'foo' in 'foo=bar'",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var nc NRIOpts
nriOpt := NewNamedNRIOptsRef(&nc)
err := nriOpt.Set(tc.cmd)
if tc.expErr != "" {
assert.Check(t, is.ErrorContains(err, tc.expErr))
return
}
assert.Check(t, err)
assert.Check(t, is.Equal(nriOpt.String(), tc.expString))
})
}
}