From e139f09ff115857d396444cc79f83907504ca979 Mon Sep 17 00:00:00 2001 From: Maksim An Date: Tue, 28 Apr 2026 15:35:39 -0700 Subject: [PATCH] plumb ResourcePartitionId to HCS - Add ResourcePartitionId field to VirtualMachine schema (flat GUID string) - Set ResourcePartitionId on HCS doc in LCOW and WCOW create paths - Extract parseResourcePartitionOptions from parseCPUOptions in builder - Add test validation that resource partition ID is set on the doc Signed-off-by: Maksim An --- internal/builder/vm/lcow/specs.go | 9 +++++- internal/builder/vm/lcow/specs_test.go | 7 ++++- internal/builder/vm/lcow/topology.go | 37 +++++++++++++++---------- internal/hcs/schema2/virtual_machine.go | 3 +- internal/uvm/create_lcow.go | 2 +- internal/uvm/create_wcow.go | 2 +- 6 files changed, 40 insertions(+), 20 deletions(-) diff --git a/internal/builder/vm/lcow/specs.go b/internal/builder/vm/lcow/specs.go index 792e80ca43..7fd6bbbfc6 100644 --- a/internal/builder/vm/lcow/specs.go +++ b/internal/builder/vm/lcow/specs.go @@ -75,6 +75,12 @@ func BuildSandboxConfig( return nil, nil, fmt.Errorf("failed to parse CPU parameters: %w", err) } + // Parse resource partition ID. + resourcePartitionID, err := parseResourcePartitionOptions(ctx, spec.Annotations, cpuConfig) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse resource partition parameters: %w", err) + } + // Parse memory configuration. memoryConfig, err := parseMemoryOptions(ctx, opts, spec.Annotations, sandboxOptions.FullyPhysicallyBacked) if err != nil { @@ -252,7 +258,8 @@ func BuildSandboxConfig( Processor: cpuConfig, Numa: numa, }, - StorageQoS: storageQOSConfig, + StorageQoS: storageQOSConfig, + ResourcePartitionId: resourcePartitionID, Devices: &hcsschema.Devices{ Scsi: scsiCtrl, VirtualPci: vpciDevices, diff --git a/internal/builder/vm/lcow/specs_test.go b/internal/builder/vm/lcow/specs_test.go index 1c168b32df..7669c921a3 100644 --- a/internal/builder/vm/lcow/specs_test.go +++ b/internal/builder/vm/lcow/specs_test.go @@ -729,7 +729,12 @@ func TestBuildSandboxConfig(t *testing.T) { shimannotations.ResourcePartitionID: "87654321-4321-8765-4321-876543218765", }, }, - // A valid GUID should be accepted without error. + validate: func(t *testing.T, doc *hcsschema.ComputeSystem, sandboxOpts *SandboxOptions) { + t.Helper() + if doc.VirtualMachine.ResourcePartitionId != "87654321-4321-8765-4321-876543218765" { + t.Errorf("expected ResourcePartitionId %q, got %q", "87654321-4321-8765-4321-876543218765", doc.VirtualMachine.ResourcePartitionId) + } + }, }, { name: "CPU group and resource partition conflict", diff --git a/internal/builder/vm/lcow/topology.go b/internal/builder/vm/lcow/topology.go index 6f8273d609..7693f90e98 100644 --- a/internal/builder/vm/lcow/topology.go +++ b/internal/builder/vm/lcow/topology.go @@ -56,21 +56,6 @@ func parseCPUOptions(ctx context.Context, opts *runhcsoptions.Options, annotatio cpu.CpuGroup = &hcsschema.CpuGroup{Id: cpuGroupID} } - // Resource Partition ID parsing. - resourcePartitionID := oci.ParseAnnotationsString(annotations, shimannotations.ResourcePartitionID, "") - if resourcePartitionID != "" { - log.G(ctx).WithField("resourcePartitionID", resourcePartitionID).Debug("setting resource partition ID") - - if _, err = guid.FromString(resourcePartitionID); err != nil { - return nil, fmt.Errorf("failed to parse resource_partition_id %q to GUID: %w", resourcePartitionID, err) - } - - // CPU group and resource partition are mutually exclusive. - if cpuGroupID != "" { - return nil, fmt.Errorf("cpu_group_id and resource_partition_id cannot be set at the same time") - } - } - log.G(ctx).WithFields(logrus.Fields{ "processorCount": count, "processorLimit": limit, @@ -81,6 +66,28 @@ func parseCPUOptions(ctx context.Context, opts *runhcsoptions.Options, annotatio return cpu, nil } +// parseResourcePartitionOptions parses the resource partition ID annotation and validates it. +// Resource partition ID and CPU group ID are mutually exclusive. +func parseResourcePartitionOptions(ctx context.Context, annotations map[string]string, cpuConfig *hcsschema.VirtualMachineProcessor) (string, error) { + resourcePartitionID := oci.ParseAnnotationsString(annotations, shimannotations.ResourcePartitionID, "") + if resourcePartitionID == "" { + return "", nil + } + + log.G(ctx).WithField("resourcePartitionID", resourcePartitionID).Debug("setting resource partition ID") + + if _, err := guid.FromString(resourcePartitionID); err != nil { + return "", fmt.Errorf("failed to parse resource_partition_id %q to GUID: %w", resourcePartitionID, err) + } + + // CPU group and resource partition are mutually exclusive. + if cpuConfig.CpuGroup != nil { + return "", fmt.Errorf("cpu_group_id and resource_partition_id cannot be set at the same time") + } + + return resourcePartitionID, nil +} + // parseMemoryOptions parses memory options from annotations and options. func parseMemoryOptions(ctx context.Context, opts *runhcsoptions.Options, annotations map[string]string, isFullyPhysicallyBacked bool) (*hcsschema.VirtualMachineMemory, error) { log.G(ctx).Debug("parseMemoryOptions: starting memory options parsing") diff --git a/internal/hcs/schema2/virtual_machine.go b/internal/hcs/schema2/virtual_machine.go index 630d1b7820..f76aa5b288 100644 --- a/internal/hcs/schema2/virtual_machine.go +++ b/internal/hcs/schema2/virtual_machine.go @@ -25,7 +25,8 @@ type VirtualMachine struct { StorageQoS *StorageQoS `json:"StorageQoS,omitempty"` DebugOptions *DebugOptions `json:"DebugOptions,omitempty"` GuestConnection *GuestConnection `json:"GuestConnection,omitempty"` - SecuritySettings *SecuritySettings `json:"SecuritySettings,omitempty"` + SecuritySettings *SecuritySettings `json:"SecuritySettings,omitempty"` + ResourcePartitionId string `json:"ResourcePartitionId,omitempty"` // Live migration options to be used on destination. MigrationOptions *MigrationInitializeOptions `json:"MigrationOptions,omitempty"` } diff --git a/internal/uvm/create_lcow.go b/internal/uvm/create_lcow.go index 9ac7018665..195c56580b 100644 --- a/internal/uvm/create_lcow.go +++ b/internal/uvm/create_lcow.go @@ -635,8 +635,8 @@ func MakeLCOWDoc(ctx context.Context, opts *OptionsLCOW, uvm *UtilityVM) (_ *hcs } if opts.ResourcePartitionID != nil { - // TODO (maksiman): assign pod to resource partition and potentially do an OS version check before that log.G(ctx).WithField("resource-partition-id", opts.ResourcePartitionID.String()).Debug("setting resource partition ID") + doc.VirtualMachine.ResourcePartitionId = opts.ResourcePartitionID.String() } // Add optional devices that were specified on the UVM spec diff --git a/internal/uvm/create_wcow.go b/internal/uvm/create_wcow.go index 7c7eb37be1..b37348132e 100644 --- a/internal/uvm/create_wcow.go +++ b/internal/uvm/create_wcow.go @@ -288,8 +288,8 @@ func prepareCommonConfigDoc(ctx context.Context, uvm *UtilityVM, opts *OptionsWC } if opts.ResourcePartitionID != nil { - // TODO (maksiman): assign pod to resource partition and potentially do an OS version check before that log.G(ctx).WithField("resource-partition-id", opts.ResourcePartitionID.String()).Debug("setting resource partition ID") + doc.VirtualMachine.ResourcePartitionId = opts.ResourcePartitionID.String() } maps.Copy(doc.VirtualMachine.Devices.HvSocket.HvSocketConfig.ServiceTable, opts.AdditionalHyperVConfig)