diff --git a/pkg/unikontainers/urunc_config.go b/pkg/unikontainers/urunc_config.go index 22573f43..38736210 100644 --- a/pkg/unikontainers/urunc_config.go +++ b/pkg/unikontainers/urunc_config.go @@ -108,6 +108,27 @@ func defaultUruncConfig() *UruncConfig { } } +// ParseUruncConfigBytes parses the urunc configuration from a byte slice (TOML format). +// If decoding fails, it returns the default configuration alongside the error. +func ParseUruncConfigBytes(data []byte) (*UruncConfig, error) { + cfg := defaultUruncConfig() + _, err := toml.Decode(string(data), cfg) + if err != nil { + return defaultUruncConfig(), err + } + for name, mon := range cfg.Monitors { + if mon.DefaultMemoryMB == 0 { + mon.DefaultMemoryMB = defaultMonitorMemoryMB + } + if mon.DefaultVCPUs == 0 { + mon.DefaultVCPUs = defaultMonitorVCPUs + } + cfg.Monitors[name] = mon + } + + return cfg, nil +} + // LoadUruncConfig loads the urunc configuration from the specified path. // If the file does not exist or is malformed, it returns the default configuration. func LoadUruncConfig(path string) (*UruncConfig, error) { diff --git a/pkg/unikontainers/urunc_config_test.go b/pkg/unikontainers/urunc_config_test.go index a51a3eb9..a6edf05b 100644 --- a/pkg/unikontainers/urunc_config_test.go +++ b/pkg/unikontainers/urunc_config_test.go @@ -618,3 +618,54 @@ path = "/usr/bin/mon" assert.Equal(t, defaultMonitorsConfig(), config.Monitors) }) } + +func TestParseUruncConfigBytes(t *testing.T) { + t.Run("valid toml bytes", func(t *testing.T) { + t.Parallel() + data := []byte(` +[log] +level = "debug" +syslog = true + +[monitors.qemu] +default_memory_mb = 512 +default_vcpus = 2 +`) + config, err := ParseUruncConfigBytes(data) + assert.NoError(t, err) + assert.Equal(t, "debug", config.Log.Level) + assert.True(t, config.Log.Syslog) + assert.Equal(t, uint(512), config.Monitors["qemu"].DefaultMemoryMB) + }) + + t.Run("invalid toml bytes returns default config and error", func(t *testing.T) { + t.Parallel() + data := []byte(`invalid toml [syntax`) + config, err := ParseUruncConfigBytes(data) + assert.Error(t, err) + assert.NotNil(t, config) + assert.Equal(t, defaultMonitorsConfig(), config.Monitors) + }) +} + +func FuzzLoadUruncConfig(f *testing.F) { + // Seed corpus with valid TOML configurations and edge cases + f.Add([]byte("")) + f.Add([]byte("[log]\nlevel = \"debug\"\nsyslog = true\n")) + f.Add([]byte("[monitors.qemu]\ndefault_memory_mb = 512\ndefault_vcpus = 2\npath = \"/usr/bin/qemu\"\n")) + f.Add([]byte("[extra_binaries.virtiofsd]\npath = \"/usr/libexec/virtiofsd\"\noptions = \"--cache always\"\n")) + f.Add([]byte("[monitors.custom]\ndefault_memory_mb = 0\ndefault_vcpus = 0\n")) + + f.Fuzz(func(t *testing.T, data []byte) { + cfg, err := ParseUruncConfigBytes(data) + if err != nil { + if cfg == nil { + t.Fatalf("ParseUruncConfigBytes returned nil config on error") + } + return + } + // Invariant: parsed config must produce valid map representation without panics + _ = cfg.Map() + }) +} +