diff --git a/config/config.go b/config/config.go index 8cb43ba2f..a7398e7cc 100644 --- a/config/config.go +++ b/config/config.go @@ -49,6 +49,12 @@ type Config struct { CPUTemplate string `json:"cpu_template"` LogLevels []string `json:"log_levels"` SmtEnabled bool `json:"smt_enabled"` + // DefaultVcpuCount is the default number of vCPUs for a microVM. + // If not specified (0), defaults to 1. + DefaultVcpuCount uint32 `json:"default_vcpu_count"` + // DefaultMemSizeMib is the default memory size in MiB for a microVM. + // Must be at least 1 MiB. If not specified (0), defaults to 128 MiB. + DefaultMemSizeMib uint32 `json:"default_mem_size_mib"` // If a CreateVM call specifies no network interfaces and DefaultNetworkInterfaces is non-empty, // the VM will default to using the network interfaces as specified here. This is especially // useful when a CNI-based network interface is provided in DefaultNetworkInterfaces. diff --git a/config/config.json.example b/config/config.json.example index 33dae8f47..889552151 100644 --- a/config/config.json.example +++ b/config/config.json.example @@ -5,5 +5,7 @@ "root_drive": "./vsock.img", "cpu_template": "T2", "log_levels": ["debug"], - "ht_enabled": false + "smt_enabled": false, + "default_vcpu_count": 1, + "default_mem_size_mib": 128 } diff --git a/config/config_test.go b/config/config_test.go index 62f81b2c0..3fa7ac122 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -76,3 +76,43 @@ func createTempConfig(t *testing.T, contents string) (string, func()) { } return configFile.Name(), func() { os.Remove(configFile.Name()) } } + +func TestLoadConfigDefaultVcpuAndMem(t *testing.T) { + testcases := []struct { + name string + configContent string + expectedVcpu uint32 + expectedMem uint32 + }{ + { + name: "DefaultsWhenNotSpecified", + configContent: `{}`, + expectedVcpu: 0, // 0 means use hardcoded default + expectedMem: 0, // 0 means use hardcoded default + }, + { + name: "CustomVcpuAndMem", + configContent: `{"default_vcpu_count": 4, "default_mem_size_mib": 512}`, + expectedVcpu: 4, + expectedMem: 512, + }, + { + name: "LargeVcpuCount", + configContent: `{"default_vcpu_count": 32}`, + expectedVcpu: 32, + expectedMem: 0, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + configFile, cleanup := createTempConfig(t, tc.configContent) + defer cleanup() + + cfg, err := LoadConfig(configFile) + assert.NoError(t, err) + assert.Equal(t, tc.expectedVcpu, cfg.DefaultVcpuCount) + assert.Equal(t, tc.expectedMem, cfg.DefaultMemSizeMib) + }) + } +} diff --git a/runtime/helpers.go b/runtime/helpers.go index df679bd13..022a4aef9 100644 --- a/runtime/helpers.go +++ b/runtime/helpers.go @@ -31,10 +31,21 @@ const ( ) func machineConfigurationFromProto(cfg *config.Config, req *proto.FirecrackerMachineConfiguration) models.MachineConfiguration { + // Use config values if provided, otherwise fall back to hardcoded defaults + vcpuCount := int64(defaultCPUCount) + if cfg.DefaultVcpuCount > 0 { + vcpuCount = int64(cfg.DefaultVcpuCount) + } + + memSizeMib := int64(defaultMemSizeMb) + if cfg.DefaultMemSizeMib > 0 { + memSizeMib = int64(cfg.DefaultMemSizeMib) + } + config := models.MachineConfiguration{ CPUTemplate: models.CPUTemplate(cfg.CPUTemplate), - VcpuCount: firecracker.Int64(defaultCPUCount), - MemSizeMib: firecracker.Int64(defaultMemSizeMb), + VcpuCount: firecracker.Int64(vcpuCount), + MemSizeMib: firecracker.Int64(memSizeMib), Smt: firecracker.Bool(cfg.SmtEnabled), } diff --git a/runtime/helpers_test.go b/runtime/helpers_test.go index affd72ec2..7d737fbe7 100644 --- a/runtime/helpers_test.go +++ b/runtime/helpers_test.go @@ -99,6 +99,69 @@ func TestMachineConfigurationFromProto(t *testing.T) { Smt: firecracker.Bool(true), }, }, + { + name: "ConfigDefaultVcpuAndMem", + config: &config.Config{ + CPUTemplate: "T2", + DefaultVcpuCount: 4, + DefaultMemSizeMib: 512, + }, + proto: nil, + expectedMachineConfig: models.MachineConfiguration{ + CPUTemplate: models.CPUTemplateT2, + VcpuCount: firecracker.Int64(4), + MemSizeMib: firecracker.Int64(512), + Smt: firecracker.Bool(false), + }, + }, + { + name: "ConfigDefaultsWithEmptyProto", + config: &config.Config{ + CPUTemplate: "T2", + DefaultVcpuCount: 8, + DefaultMemSizeMib: 1024, + }, + proto: &proto.FirecrackerMachineConfiguration{}, + expectedMachineConfig: models.MachineConfiguration{ + CPUTemplate: models.CPUTemplateT2, + VcpuCount: firecracker.Int64(8), + MemSizeMib: firecracker.Int64(1024), + Smt: firecracker.Bool(false), + }, + }, + { + name: "ProtoOverridesConfigDefaults", + config: &config.Config{ + CPUTemplate: "T2", + DefaultVcpuCount: 4, + DefaultMemSizeMib: 512, + }, + proto: &proto.FirecrackerMachineConfiguration{ + VcpuCount: 16, + MemSizeMib: 2048, + }, + expectedMachineConfig: models.MachineConfiguration{ + CPUTemplate: models.CPUTemplateT2, + VcpuCount: firecracker.Int64(16), + MemSizeMib: firecracker.Int64(2048), + Smt: firecracker.Bool(false), + }, + }, + { + name: "ZeroConfigDefaultsFallbackToHardcoded", + config: &config.Config{ + CPUTemplate: "T2", + DefaultVcpuCount: 0, + DefaultMemSizeMib: 0, + }, + proto: nil, + expectedMachineConfig: models.MachineConfiguration{ + CPUTemplate: models.CPUTemplateT2, + VcpuCount: firecracker.Int64(defaultCPUCount), + MemSizeMib: firecracker.Int64(defaultMemSizeMb), + Smt: firecracker.Bool(false), + }, + }, } for _, tc := range testcases {