Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions cmd/nvidia-ctk/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ func getStruct(current reflect.Type, paths ...string) (reflect.StructField, erro
return reflect.StructField{}, fmt.Errorf("%w: no fields selected", errUndefinedField)
}
tomlField := paths[0]
if current.Kind() != reflect.Struct {
return reflect.StructField{}, fmt.Errorf("%w: %q", errUndefinedField, tomlField)
}
for f := range current.Fields() {
v, ok := f.Tag.Lookup("toml")
if !ok {
Expand Down
145 changes: 145 additions & 0 deletions cmd/nvidia-ctk/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
package config

import (
"os"
"path/filepath"
"testing"

"github.com/pelletier/go-toml"
"github.com/stretchr/testify/require"
)

Expand All @@ -43,6 +46,65 @@ func TestSetFlagToKeyValue(t *testing.T) {
expectedKey: "nvidia-container-cli.undefined",
expectedError: errInvalidConfigOption,
},
{
description: "child of boolean option returns error",
setFlag: "disable-require.extra=true",
expectedKey: "disable-require.extra",
expectedError: errInvalidConfigOption,
},
{
description: "child of string option returns error",
setFlag: "nvidia-container-cli.path.extra=/tmp/cli",
expectedKey: "nvidia-container-cli.path.extra",
expectedError: errInvalidConfigOption,
},
{
description: "child of slice option returns error",
setFlag: "nvidia-container-cli.environment.extra=VALUE",
expectedKey: "nvidia-container-cli.environment.extra",
expectedError: errInvalidConfigOption,
},
{
description: "child of pointer option returns error",
setFlag: "features.allow-ldconfig-from-container.extra=true",
expectedKey: "features.allow-ldconfig-from-container.extra",
expectedError: errInvalidConfigOption,
},
{
description: "nested string option returns value",
setFlag: "nvidia-container-cli.path=/tmp/cli",
expectedKey: "nvidia-container-cli.path",
expectedValue: "/tmp/cli",
},
{
description: "deeply nested string option returns value",
setFlag: "nvidia-container-runtime.modes.cdi.default-kind=example.com/gpu",
expectedKey: "nvidia-container-runtime.modes.cdi.default-kind",
expectedValue: "example.com/gpu",
},
{
description: "pointer boolean option assumes true",
setFlag: "features.allow-ldconfig-from-container",
expectedKey: "features.allow-ldconfig-from-container",
expectedValue: true,
},
{
description: "pointer boolean option returns true",
setFlag: "features.allow-ldconfig-from-container=true",
expectedKey: "features.allow-ldconfig-from-container",
expectedValue: true,
},
{
description: "pointer boolean option returns false",
setFlag: "features.allow-ldconfig-from-container=false",
expectedKey: "features.allow-ldconfig-from-container",
expectedValue: false,
},
{
description: "pointer boolean option returns nil",
setFlag: "features.allow-ldconfig-from-container=nil",
expectedKey: "features.allow-ldconfig-from-container",
},
{
description: "boolean option assumes true",
setFlag: "disable-require",
Expand Down Expand Up @@ -140,3 +202,86 @@ func TestSetFlagToKeyValue(t *testing.T) {
})
}
}

func TestConfigCommandSet(t *testing.T) {
testCases := map[string]struct {
setFlag string
expectedKey string
expectedValue any
expectedError error
}{
"child of boolean": {
setFlag: "disable-require.extra=true",
expectedError: errInvalidConfigOption,
},
"child of string": {
setFlag: "nvidia-container-cli.path.extra=/tmp/cli",
expectedError: errInvalidConfigOption,
},
"child of slice": {
setFlag: "nvidia-container-cli.environment.extra=VALUE",
expectedError: errInvalidConfigOption,
},
"child of pointer": {
setFlag: "features.allow-ldconfig-from-container.extra=true",
expectedError: errInvalidConfigOption,
},
"nested string": {
setFlag: "nvidia-container-cli.path=/tmp/cli",
expectedKey: "nvidia-container-cli.path",
expectedValue: "/tmp/cli",
},
"deeply nested string": {
setFlag: "nvidia-container-runtime.modes.cdi.default-kind=example.com/gpu",
expectedKey: "nvidia-container-runtime.modes.cdi.default-kind",
expectedValue: "example.com/gpu",
},
"slice": {
setFlag: "nvidia-container-cli.environment=FIRST=1:SECOND=2",
expectedKey: "nvidia-container-cli.environment",
expectedValue: []any{"FIRST=1", "SECOND=2"},
},
"pointer boolean assumes true": {
setFlag: "features.allow-ldconfig-from-container",
expectedKey: "features.allow-ldconfig-from-container",
expectedValue: true,
},
"pointer boolean false": {
setFlag: "features.allow-ldconfig-from-container=false",
expectedKey: "features.allow-ldconfig-from-container",
expectedValue: false,
},
"pointer boolean nil": {
setFlag: "features.allow-ldconfig-from-container=nil",
expectedKey: "features.allow-ldconfig-from-container",
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
configPath := filepath.Join(t.TempDir(), "config.toml")
original := []byte("disable-require = false\n[features]\nallow-ldconfig-from-container = true\n")
require.NoError(t, os.WriteFile(configPath, original, 0600))

var err error
require.NotPanics(t, func() {
cmd := NewCommand(nil)
err = cmd.Run(t.Context(), []string{"config", "--config-file", configPath, "--in-place", "--set", tc.setFlag})
})
require.ErrorIs(t, err, tc.expectedError)

contents, readErr := os.ReadFile(configPath)
require.NoError(t, readErr)
if tc.expectedError != nil {
require.ErrorIs(t, err, errUndefinedField)
require.Contains(t, err.Error(), "invalid --set option "+tc.setFlag)
require.Equal(t, original, contents)
return
}

cfg, err := toml.LoadBytes(contents)
require.NoError(t, err)
require.Equal(t, tc.expectedValue, cfg.Get(tc.expectedKey))
})
}
}