diff --git a/config/config.go b/config/config.go index c8d87c65..20fe027b 100644 --- a/config/config.go +++ b/config/config.go @@ -4,12 +4,9 @@ package config import ( "errors" "fmt" - "os" - "path/filepath" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/yaml" ) // Config provides gpud configuration data for the server @@ -103,37 +100,3 @@ func (config *Config) Validate() error { } return nil } - -func (config *Config) YAML() ([]byte, error) { - return yaml.Marshal(config) -} - -func (config *Config) SyncYAML(file string) error { - if _, err := os.Stat(filepath.Dir(file)); os.IsNotExist(err) { - if err = os.MkdirAll(filepath.Dir(file), 0755); err != nil { - return err - } - } - data, err := config.YAML() - if err != nil { - return err - } - return os.WriteFile(file, data, 0644) -} - -func LoadConfigYAML(file string) (*Config, error) { - data, err := os.ReadFile(file) - if err != nil { - return nil, err - } - return ParseConfigYAML(data) -} - -func ParseConfigYAML(data []byte) (*Config, error) { - config := new(Config) - err := yaml.Unmarshal(data, config) - if err != nil { - return nil, err - } - return config, nil -} diff --git a/config/config_test.go b/config/config_test.go index c6151947..fd5a977f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -57,41 +57,3 @@ func TestConfigValidate_AutoUpdateExitCode(t *testing.T) { }) } } - -func TestLoadConfigYAML(t *testing.T) { - t.Parallel() - - config, err := LoadConfigYAML("testdata/test.0.yaml") - if err != nil { - t.Fatalf("failed to parse config: %v", err) - } - - if config.Components["systemd"] == nil { - t.Fatalf("systemd component is nil") - } - - b, err := config.YAML() - if err != nil { - t.Fatalf("failed to marshal config: %v", err) - } - t.Logf("config:\n%s", string(b)) -} - -func TestLoadConfigYAMLNull(t *testing.T) { - t.Parallel() - - config, err := LoadConfigYAML("testdata/test.1.gpu.yaml") - if err != nil { - t.Fatalf("failed to parse config: %v", err) - } - for k, v := range config.Components { - if v != nil { - t.Errorf("key: %s, value: %v", k, v) - } - } - b, err := config.YAML() - if err != nil { - t.Fatalf("failed to marshal config: %v", err) - } - t.Logf("config:\n%s", string(b)) -} diff --git a/config/op_options.go b/config/options.go similarity index 100% rename from config/op_options.go rename to config/options.go diff --git a/config/options_test.go b/config/options_test.go new file mode 100644 index 00000000..1d4ceefe --- /dev/null +++ b/config/options_test.go @@ -0,0 +1,225 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOp_ApplyOpts(t *testing.T) { + t.Run("empty options", func(t *testing.T) { + op := &Op{} + err := op.ApplyOpts(nil) + assert.NoError(t, err) + }) + + t.Run("multiple options", func(t *testing.T) { + op := &Op{} + err := op.ApplyOpts([]OpOption{ + WithFilesToCheck("/path1", "/path2"), + WithKernelModulesToCheck("mod1", "mod2"), + WithDockerIgnoreConnectionErrors(true), + WithKubeletIgnoreConnectionErrors(true), + }) + require.NoError(t, err) + assert.Equal(t, []string{"/path1", "/path2"}, op.FilesToCheck) + assert.Equal(t, []string{"mod1", "mod2"}, op.KernelModulesToCheck) + assert.True(t, op.DockerIgnoreConnectionErrors) + assert.True(t, op.KubeletIgnoreConnectionErrors) + }) +} + +func TestWithFilesToCheck(t *testing.T) { + tests := []struct { + name string + initialFiles []string + filesToAdd []string + expectedFiles []string + }{ + { + name: "add to empty", + initialFiles: nil, + filesToAdd: []string{"/path1", "/path2"}, + expectedFiles: []string{"/path1", "/path2"}, + }, + { + name: "add to existing", + initialFiles: []string{"/existing"}, + filesToAdd: []string{"/new1", "/new2"}, + expectedFiles: []string{"/existing", "/new1", "/new2"}, + }, + { + name: "empty addition", + initialFiles: []string{"/existing"}, + filesToAdd: []string{}, + expectedFiles: []string{"/existing"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{FilesToCheck: tt.initialFiles} + opt := WithFilesToCheck(tt.filesToAdd...) + opt(op) + assert.Equal(t, tt.expectedFiles, op.FilesToCheck) + }) + } +} + +func TestWithKernelModulesToCheck(t *testing.T) { + tests := []struct { + name string + initialModules []string + modulesToAdd []string + expectedModules []string + }{ + { + name: "add to empty", + initialModules: nil, + modulesToAdd: []string{"mod1", "mod2"}, + expectedModules: []string{"mod1", "mod2"}, + }, + { + name: "add to existing", + initialModules: []string{"existing"}, + modulesToAdd: []string{"new1", "new2"}, + expectedModules: []string{"existing", "new1", "new2"}, + }, + { + name: "empty addition", + initialModules: []string{"existing"}, + modulesToAdd: []string{}, + expectedModules: []string{"existing"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{KernelModulesToCheck: tt.initialModules} + opt := WithKernelModulesToCheck(tt.modulesToAdd...) + opt(op) + assert.Equal(t, tt.expectedModules, op.KernelModulesToCheck) + }) + } +} + +func TestWithDockerIgnoreConnectionErrors(t *testing.T) { + tests := []struct { + name string + value bool + expected bool + }{ + {"set true", true, true}, + {"set false", false, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{} + opt := WithDockerIgnoreConnectionErrors(tt.value) + opt(op) + assert.Equal(t, tt.expected, op.DockerIgnoreConnectionErrors) + }) + } +} + +func TestWithKubeletIgnoreConnectionErrors(t *testing.T) { + tests := []struct { + name string + value bool + expected bool + }{ + {"set true", true, true}, + {"set false", false, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{} + opt := WithKubeletIgnoreConnectionErrors(tt.value) + opt(op) + assert.Equal(t, tt.expected, op.KubeletIgnoreConnectionErrors) + }) + } +} + +func TestWithNvidiaSMICommand(t *testing.T) { + tests := []struct { + name string + path string + expected string + }{ + {"set custom path", "/usr/local/bin/nvidia-smi", "/usr/local/bin/nvidia-smi"}, + {"set empty path", "", ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{} + opt := WithNvidiaSMICommand(tt.path) + opt(op) + assert.Equal(t, tt.expected, op.NvidiaSMICommand) + }) + } +} + +func TestWithNvidiaSMIQueryCommand(t *testing.T) { + tests := []struct { + name string + path string + expected string + }{ + {"set custom path", "/usr/local/bin/nvidia-smi-query", "/usr/local/bin/nvidia-smi-query"}, + {"set empty path", "", ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{} + opt := WithNvidiaSMIQueryCommand(tt.path) + opt(op) + assert.Equal(t, tt.expected, op.NvidiaSMIQueryCommand) + }) + } +} + +func TestWithIbstatCommand(t *testing.T) { + tests := []struct { + name string + path string + expected string + }{ + {"set custom path", "/usr/local/bin/ibstat", "/usr/local/bin/ibstat"}, + {"set empty path", "", ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{} + opt := WithIbstatCommand(tt.path) + opt(op) + assert.Equal(t, tt.expected, op.IbstatCommand) + }) + } +} + +func TestWithInfinibandClassDirectory(t *testing.T) { + tests := []struct { + name string + path string + expected string + }{ + {"set custom path", "/sys/class/infiniband", "/sys/class/infiniband"}, + {"set empty path", "", ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := &Op{} + opt := WithInfinibandClassDirectory(tt.path) + opt(op) + assert.Equal(t, tt.expected, op.InfinibandClassDirectory) + }) + } +}