diff --git a/api/compute/v1alpha1/machine_types.go b/api/compute/v1alpha1/machine_types.go index 23a1b05b4..1cf681a0e 100644 --- a/api/compute/v1alpha1/machine_types.go +++ b/api/compute/v1alpha1/machine_types.go @@ -23,6 +23,7 @@ type MachineSpec struct { // Power is the desired machine power state. // Defaults to PowerOn. Power Power `json:"power,omitempty"` + // Deprecated: Use LocalDisk to provide a bootable disk // Image is the optional URL providing the operating system image of the machine. // +optional Image string `json:"image,omitempty"` @@ -106,8 +107,11 @@ type Volume struct { type VolumeSource struct { // VolumeRef instructs to use the specified Volume as source for the attachment. VolumeRef *corev1.LocalObjectReference `json:"volumeRef,omitempty"` + // Deprecated: Use LocalDisk instead // EmptyDisk instructs to use a Volume offered by the machine pool provider. EmptyDisk *EmptyDiskVolumeSource `json:"emptyDisk,omitempty"` + // LocalDisk instructs to use a Volume offered by the machine pool provider. + LocalDisk *LocalDiskVolumeSource `json:"localDisk,omitempty"` // Ephemeral instructs to create an ephemeral (i.e. coupled to the lifetime of the surrounding object) // Volume to use. Ephemeral *EphemeralVolumeSource `json:"ephemeral,omitempty"` @@ -122,6 +126,18 @@ type EmptyDiskVolumeSource struct { SizeLimit *resource.Quantity `json:"sizeLimit,omitempty"` } +// LocalDiskVolumeSource is a volume that's offered by the machine pool provider. +// Usually ephemeral (i.e. deleted when the surrounding entity is deleted), with +// varying performance characteristics. Potentially not recoverable. +type LocalDiskVolumeSource struct { + // SizeLimit is the total amount of local storage required for this LocalDisk volume. + // The default is nil which means that the limit is undefined. + SizeLimit *resource.Quantity `json:"sizeLimit,omitempty"` + // Image is the optional URL providing the operating system image of the machine. + // +optional + Image string `json:"image,omitempty"` +} + // NetworkInterfaceStatus reports the status of a NetworkInterfaceSource. type NetworkInterfaceStatus struct { // Name is the name of the NetworkInterface to whom the status belongs to. diff --git a/api/compute/v1alpha1/zz_generated.deepcopy.go b/api/compute/v1alpha1/zz_generated.deepcopy.go index 7910e66dd..c62bdb9f6 100644 --- a/api/compute/v1alpha1/zz_generated.deepcopy.go +++ b/api/compute/v1alpha1/zz_generated.deepcopy.go @@ -112,6 +112,27 @@ func (in *EphemeralVolumeSource) DeepCopy() *EphemeralVolumeSource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalDiskVolumeSource) DeepCopyInto(out *LocalDiskVolumeSource) { + *out = *in + if in.SizeLimit != nil { + in, out := &in.SizeLimit, &out.SizeLimit + x := (*in).DeepCopy() + *out = &x + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalDiskVolumeSource. +func (in *LocalDiskVolumeSource) DeepCopy() *LocalDiskVolumeSource { + if in == nil { + return nil + } + out := new(LocalDiskVolumeSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Machine) DeepCopyInto(out *Machine) { *out = *in @@ -636,6 +657,11 @@ func (in *VolumeSource) DeepCopyInto(out *VolumeSource) { *out = new(EmptyDiskVolumeSource) (*in).DeepCopyInto(*out) } + if in.LocalDisk != nil { + in, out := &in.LocalDisk, &out.LocalDisk + *out = new(LocalDiskVolumeSource) + (*in).DeepCopyInto(*out) + } if in.Ephemeral != nil { in, out := &in.Ephemeral, &out.Ephemeral *out = new(EphemeralVolumeSource) diff --git a/broker/machinebroker/server/event_list_test.go b/broker/machinebroker/server/event_list_test.go index 101e6bb8c..2eef19ee8 100644 --- a/broker/machinebroker/server/event_list_test.go +++ b/broker/machinebroker/server/event_list_test.go @@ -44,10 +44,15 @@ var _ = Describe("ListEvents", func() { }, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, + Volumes: []*iri.Volume{{ + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, NetworkInterfaces: []*iri.NetworkInterface{ { Name: "primary-nic", diff --git a/broker/machinebroker/server/machine.go b/broker/machinebroker/server/machine.go index 9149c4d3d..b280566db 100644 --- a/broker/machinebroker/server/machine.go +++ b/broker/machinebroker/server/machine.go @@ -115,7 +115,7 @@ func (s *Server) convertIronCoreVolume( ) (*iri.Volume, error) { var ( connection *iri.VolumeConnection - emptyDisk *iri.EmptyDisk + localDisk *iri.LocalDisk ) switch { case ironcoreMachineVolume.VolumeRef != nil: @@ -137,22 +137,31 @@ func (s *Server) convertIronCoreVolume( EffectiveStorageBytes: effectiveStorageBytes, } } - case ironcoreMachineVolume.EmptyDisk != nil: + case ironcoreMachineVolume.LocalDisk != nil: var sizeBytes int64 - if sizeLimit := ironcoreMachineVolume.EmptyDisk.SizeLimit; sizeLimit != nil { + if sizeLimit := ironcoreMachineVolume.LocalDisk.SizeLimit; sizeLimit != nil { sizeBytes = sizeLimit.Value() } - emptyDisk = &iri.EmptyDisk{ + + var imageSpec *iri.ImageSpec + if image := ironcoreMachineVolume.LocalDisk.Image; image != "" { + imageSpec = &iri.ImageSpec{ + Image: ironcoreMachineVolume.LocalDisk.Image, + } + } + + localDisk = &iri.LocalDisk{ SizeBytes: sizeBytes, + Image: imageSpec, } default: - return nil, fmt.Errorf("machine volume %#v does neither specify volume ref nor empty disk", ironcoreMachineVolume) + return nil, fmt.Errorf("machine volume %#v does neither specify volume ref nor local disk", ironcoreMachineVolume) } return &iri.Volume{ Name: ironcoreMachineVolume.Name, Device: *ironcoreMachineVolume.Device, - EmptyDisk: emptyDisk, + LocalDisk: localDisk, Connection: connection, }, nil } @@ -195,13 +204,6 @@ func (s *Server) convertAggregateIronCoreMachine(aggIronCoreMachine *AggregateIr return nil, fmt.Errorf("error converting power state: %w", err) } - var imageSpec *iri.ImageSpec - if image := aggIronCoreMachine.Machine.Spec.Image; image != "" { - imageSpec = &iri.ImageSpec{ - Image: image, - } - } - volumes := make([]*iri.Volume, len(aggIronCoreMachine.Machine.Spec.Volumes)) for i, ironcoreMachineVolume := range aggIronCoreMachine.Machine.Spec.Volumes { ironcoreVolume := aggIronCoreMachine.Volumes[ironcoreMachineVolume.Name] @@ -253,7 +255,6 @@ func (s *Server) convertAggregateIronCoreMachine(aggIronCoreMachine *AggregateIr Metadata: metadata, Spec: &iri.MachineSpec{ Power: power, - Image: imageSpec, Class: aggIronCoreMachine.Machine.Spec.MachineClassRef.Name, IgnitionData: ignitionData, Volumes: volumes, diff --git a/broker/machinebroker/server/machine_create.go b/broker/machinebroker/server/machine_create.go index 084db6205..3632c9077 100644 --- a/broker/machinebroker/server/machine_create.go +++ b/broker/machinebroker/server/machine_create.go @@ -28,7 +28,6 @@ type IronCoreMachineConfig struct { Annotations map[string]string Power computev1alpha1.Power MachineClassName string - Image string IgnitionData []byte NetworkInterfaceConfigs []*IronCoreNetworkInterfaceConfig VolumeConfigs []*IronCoreVolumeConfig @@ -75,11 +74,6 @@ func (s *Server) getIronCoreMachineConfig(machine *iri.Machine) (*IronCoreMachin return nil, err } - var ironcoreImage string - if image := machine.Spec.Image; image != nil { - ironcoreImage = image.Image - } - ironcoreNicCfgs := make([]*IronCoreNetworkInterfaceConfig, len(machine.Spec.NetworkInterfaces)) for i, nic := range machine.Spec.NetworkInterfaces { ironcoreNicCfg, err := s.getIronCoreNetworkInterfaceConfig(nic) @@ -116,7 +110,6 @@ func (s *Server) getIronCoreMachineConfig(machine *iri.Machine) (*IronCoreMachin Annotations: annotations, Power: ironcorePower, MachineClassName: machine.Spec.Class, - Image: ironcoreImage, IgnitionData: machine.Spec.IgnitionData, NetworkInterfaceConfigs: ironcoreNicCfgs, VolumeConfigs: ironcoreVolumeCfgs, @@ -199,7 +192,6 @@ func (s *Server) createIronCoreMachine( MachinePoolSelector: s.cluster.MachinePoolSelector(), MachinePoolRef: s.ironcoreMachinePoolRef(), Power: cfg.Power, - Image: cfg.Image, ImagePullSecretRef: nil, // TODO: Specify as soon as available. NetworkInterfaces: ironcoreMachineNics, Volumes: ironcoreMachineVolumes, diff --git a/broker/machinebroker/server/machine_create_test.go b/broker/machinebroker/server/machine_create_test.go index f4f3e0498..eaabc8721 100644 --- a/broker/machinebroker/server/machine_create_test.go +++ b/broker/machinebroker/server/machine_create_test.go @@ -31,10 +31,15 @@ var _ = Describe("CreateMachine", func() { }, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, + Volumes: []*iri.Volume{{ + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, }, }, }) @@ -64,7 +69,6 @@ var _ = Describe("CreateMachine", func() { machinebrokerv1alpha1.LabelsAnnotation: encodedIRILabels, })) Expect(ironcoreMachine.Spec.Power).To(Equal(computev1alpha1.PowerOn)) - Expect(ironcoreMachine.Spec.Image).To(Equal("example.org/foo:latest")) Expect(ironcoreMachine.Spec.MachineClassRef.Name).To(Equal(machineClass.Name)) }) }) diff --git a/broker/machinebroker/server/machine_delete_test.go b/broker/machinebroker/server/machine_delete_test.go index 0928a45dc..6c8d5f3a1 100644 --- a/broker/machinebroker/server/machine_delete_test.go +++ b/broker/machinebroker/server/machine_delete_test.go @@ -31,10 +31,8 @@ var _ = Describe("DeleteMachine", func() { }, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, + NetworkInterfaces: []*iri.NetworkInterface{ { Name: "primary-nic", @@ -43,6 +41,14 @@ var _ = Describe("DeleteMachine", func() { }, }, Volumes: []*iri.Volume{ + { + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }, { Name: "primary-volume", Device: "oda", diff --git a/broker/machinebroker/server/machine_list_test.go b/broker/machinebroker/server/machine_list_test.go index 435ba238d..e180b608a 100644 --- a/broker/machinebroker/server/machine_list_test.go +++ b/broker/machinebroker/server/machine_list_test.go @@ -30,9 +30,6 @@ var _ = Describe("ListMachines", func() { }, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, NetworkInterfaces: []*iri.NetworkInterface{ { @@ -41,7 +38,15 @@ var _ = Describe("ListMachines", func() { Ips: []string{"10.0.0.1"}, }, }, - Volumes: []*iri.Volume{ + + Volumes: []*iri.Volume{{ + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }, { Name: "primary-volume", Device: "oda", diff --git a/broker/machinebroker/server/machine_networkinterface_attach_test.go b/broker/machinebroker/server/machine_networkinterface_attach_test.go index 0aa5606fb..5cd09d7ba 100644 --- a/broker/machinebroker/server/machine_networkinterface_attach_test.go +++ b/broker/machinebroker/server/machine_networkinterface_attach_test.go @@ -31,10 +31,15 @@ var _ = Describe("AttachNetworkInterface", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, + Volumes: []*iri.Volume{{ + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, }, }, }) @@ -116,9 +121,14 @@ var _ = Describe("AttachNetworkInterface", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, + Volumes: []*iri.Volume{{ + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, Class: machineClass.Name, }, }, diff --git a/broker/machinebroker/server/machine_networkinterface_detach_test.go b/broker/machinebroker/server/machine_networkinterface_detach_test.go index 27825429a..daba0000a 100644 --- a/broker/machinebroker/server/machine_networkinterface_detach_test.go +++ b/broker/machinebroker/server/machine_networkinterface_detach_test.go @@ -23,9 +23,14 @@ var _ = Describe("DetachNetworkInterface", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, + Volumes: []*iri.Volume{{ + Name: "root", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, Class: machineClass.Name, NetworkInterfaces: []*iri.NetworkInterface{ { diff --git a/broker/machinebroker/server/machine_volume_attach.go b/broker/machinebroker/server/machine_volume_attach.go index bad2ceedd..a0834e111 100644 --- a/broker/machinebroker/server/machine_volume_attach.go +++ b/broker/machinebroker/server/machine_volume_attach.go @@ -30,13 +30,14 @@ import ( type IronCoreVolumeConfig struct { Name string Device string - EmptyDisk *IronCoreVolumeEmptyDiskConfig + LocalDisk *IronCoreVolumeLocalDiskConfig Remote *IronCoreVolumeRemoteConfig Labels map[string]string } -type IronCoreVolumeEmptyDiskConfig struct { +type IronCoreVolumeLocalDiskConfig struct { SizeLimit *resource.Quantity + Image string } type IronCoreVolumeRemoteConfig struct { @@ -50,17 +51,18 @@ type IronCoreVolumeRemoteConfig struct { func (s *Server) getIronCoreVolumeConfig(volume *iri.Volume) (*IronCoreVolumeConfig, error) { var ( - emptyDisk *IronCoreVolumeEmptyDiskConfig + localDisk *IronCoreVolumeLocalDiskConfig remote *IronCoreVolumeRemoteConfig ) switch { - case volume.EmptyDisk != nil: + case volume.LocalDisk != nil: var sizeLimit *resource.Quantity - if sizeBytes := volume.EmptyDisk.SizeBytes; sizeBytes > 0 { + if sizeBytes := volume.LocalDisk.SizeBytes; sizeBytes > 0 { sizeLimit = resource.NewQuantity(sizeBytes, resource.DecimalSI) } - emptyDisk = &IronCoreVolumeEmptyDiskConfig{ + localDisk = &IronCoreVolumeLocalDiskConfig{ SizeLimit: sizeLimit, + Image: volume.LocalDisk.Image.Image, } case volume.Connection != nil: remote = &IronCoreVolumeRemoteConfig{ @@ -81,7 +83,7 @@ func (s *Server) getIronCoreVolumeConfig(volume *iri.Volume) (*IronCoreVolumeCon return &IronCoreVolumeConfig{ Name: volume.Name, Device: volume.Device, - EmptyDisk: emptyDisk, + LocalDisk: localDisk, Remote: remote, Labels: labels, }, nil @@ -236,9 +238,10 @@ func (s *Server) createIronCoreVolume( AccessSecret: accessSecret, } ironcoreVolumeSrc.VolumeRef = &corev1.LocalObjectReference{Name: ironcoreVolume.Name} - case cfg.EmptyDisk != nil: - ironcoreVolumeSrc.EmptyDisk = &computev1alpha1.EmptyDiskVolumeSource{ - SizeLimit: cfg.EmptyDisk.SizeLimit, + case cfg.LocalDisk != nil: + ironcoreVolumeSrc.LocalDisk = &computev1alpha1.LocalDiskVolumeSource{ + SizeLimit: cfg.LocalDisk.SizeLimit, + Image: cfg.LocalDisk.Image, } } return &computev1alpha1.Volume{ diff --git a/broker/machinebroker/server/machine_volume_attach_test.go b/broker/machinebroker/server/machine_volume_attach_test.go index 81c06abb0..42c9cb95a 100644 --- a/broker/machinebroker/server/machine_volume_attach_test.go +++ b/broker/machinebroker/server/machine_volume_attach_test.go @@ -29,9 +29,15 @@ var _ = Describe("AttachVolume", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, + Volumes: []*iri.Volume{{ + Name: "root", + Device: "oda", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, Class: machineClass.Name, }, }, @@ -47,7 +53,7 @@ var _ = Describe("AttachVolume", func() { MachineId: machineID, Volume: &iri.Volume{ Name: "my-volume", - Device: "oda", + Device: "odb", Connection: &iri.VolumeConnection{ Driver: "ceph", Handle: "mycephvolume", @@ -69,9 +75,9 @@ var _ = Describe("AttachVolume", func() { Expect(k8sClient.Get(ctx, ironcoreMachineKey, ironcoreMachine)).To(Succeed()) By("inspecting the ironcore machine's volumes") - Expect(ironcoreMachine.Spec.Volumes).To(ConsistOf(MatchAllFields(Fields{ + Expect(ironcoreMachine.Spec.Volumes).To(ContainElement(MatchAllFields(Fields{ "Name": Equal("my-volume"), - "Device": PointTo(Equal("oda")), + "Device": PointTo(Equal("odb")), "VolumeSource": MatchFields(IgnoreExtras, Fields{ "VolumeRef": PointTo(MatchAllFields(Fields{ "Name": Not(BeEmpty()), @@ -81,7 +87,7 @@ var _ = Describe("AttachVolume", func() { By("getting the corresponding ironcore volume") volume := &storagev1alpha1.Volume{} - volumeName := ironcoreMachine.Spec.Volumes[0].VolumeRef.Name + volumeName := ironcoreMachine.Spec.Volumes[1].VolumeRef.Name volumeKey := client.ObjectKey{Namespace: ns.Name, Name: volumeName} Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed()) @@ -124,9 +130,15 @@ var _ = Describe("AttachVolume", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, + Volumes: []*iri.Volume{{ + Name: "root", + Device: "oda", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }}, Class: machineClass.Name, }, }, @@ -139,7 +151,7 @@ var _ = Describe("AttachVolume", func() { MachineId: machineID, Volume: &iri.Volume{ Name: "my-volume", - Device: "oda", + Device: "odb", Connection: &iri.VolumeConnection{ Driver: "ceph", Handle: "mycephvolume", @@ -162,9 +174,9 @@ var _ = Describe("AttachVolume", func() { Expect(k8sClient.Get(ctx, ironcoreMachineKey, ironcoreMachine)).To(Succeed()) By("inspecting the ironcore machine's volumes") - Expect(ironcoreMachine.Spec.Volumes).To(ConsistOf(MatchAllFields(Fields{ + Expect(ironcoreMachine.Spec.Volumes).To(ContainElement(MatchAllFields(Fields{ "Name": Equal("my-volume"), - "Device": PointTo(Equal("oda")), + "Device": PointTo(Equal("odb")), "VolumeSource": MatchFields(IgnoreExtras, Fields{ "VolumeRef": PointTo(MatchAllFields(Fields{ "Name": Not(BeEmpty()), @@ -174,7 +186,7 @@ var _ = Describe("AttachVolume", func() { By("getting the corresponding ironcore volume") volume := &storagev1alpha1.Volume{} - volumeName := ironcoreMachine.Spec.Volumes[0].VolumeRef.Name + volumeName := ironcoreMachine.Spec.Volumes[1].VolumeRef.Name volumeKey := client.ObjectKey{Namespace: ns.Name, Name: volumeName} Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed()) diff --git a/broker/machinebroker/server/machine_volume_detach.go b/broker/machinebroker/server/machine_volume_detach.go index 81972f39e..706bd6dc3 100644 --- a/broker/machinebroker/server/machine_volume_detach.go +++ b/broker/machinebroker/server/machine_volume_detach.go @@ -55,8 +55,8 @@ func (s *Server) DetachVolume(ctx context.Context, req *iri.DetachVolumeRequest) if err := s.cluster.Client().Delete(ctx, ironcoreVolume); client.IgnoreNotFound(err) != nil { return nil, fmt.Errorf("error deleting ironcore volume %s: %w", ironcoreVolumeName, err) } - case ironcoreMachineVolume.EmptyDisk != nil: - log.V(1).Info("No need to clean up empty disk") + case ironcoreMachineVolume.LocalDisk != nil: + log.V(1).Info("No need to clean up local disk") default: return nil, fmt.Errorf("unrecognized ironcore machine volume %#v", ironcoreMachineVolume) } diff --git a/broker/machinebroker/server/machine_volume_detach_test.go b/broker/machinebroker/server/machine_volume_detach_test.go index 15edb54d4..9d970bd83 100644 --- a/broker/machinebroker/server/machine_volume_detach_test.go +++ b/broker/machinebroker/server/machine_volume_detach_test.go @@ -11,6 +11,7 @@ import ( . "github.com/onsi/gomega" . "github.com/onsi/gomega/gstruct" corev1 "k8s.io/api/core/v1" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -24,14 +25,19 @@ var _ = Describe("DetachVolume", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, - Volumes: []*iri.Volume{ + Volumes: []*iri.Volume{{ + Name: "root", + Device: "oda", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }, { Name: "my-volume", - Device: "oda", + Device: "odb", Connection: &iri.VolumeConnection{ Driver: "ceph", Handle: "mycephvolume", @@ -62,7 +68,15 @@ var _ = Describe("DetachVolume", func() { Expect(k8sClient.Get(ctx, ironcoreMachineKey, ironcoreMachine)).To(Succeed()) By("inspecting the ironcore machine's volumes") - Expect(ironcoreMachine.Spec.Volumes).To(BeEmpty()) + Expect(ironcoreMachine.Spec.Volumes).To(ContainElement(computev1alpha1.Volume{ + Name: "root", + Device: ptr.To("oda"), + VolumeSource: computev1alpha1.VolumeSource{ + LocalDisk: &computev1alpha1.LocalDiskVolumeSource{ + Image: "example.org/foo:latest", + }, + }, + })) By("listing for any ironcore volume in the namespace") volumeList := &storagev1alpha1.VolumeList{} diff --git a/broker/machinebroker/server/machine_volume_update_test.go b/broker/machinebroker/server/machine_volume_update_test.go index 284bde254..eb42d174c 100644 --- a/broker/machinebroker/server/machine_volume_update_test.go +++ b/broker/machinebroker/server/machine_volume_update_test.go @@ -30,14 +30,20 @@ var _ = Describe("UpdateVolume", func() { Machine: &iri.Machine{ Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, - Image: &iri.ImageSpec{ - Image: "example.org/foo:latest", - }, Class: machineClass.Name, - Volumes: []*iri.Volume{ + + Volumes: []*iri.Volume{{ + Name: "root", + Device: "oda", + LocalDisk: &iri.LocalDisk{ + Image: &iri.ImageSpec{ + Image: "example.org/foo:latest", + }, + }, + }, { Name: "primary", - Device: "oda", + Device: "odb", Connection: &iri.VolumeConnection{ Driver: "test", Handle: "testhandle", @@ -58,7 +64,7 @@ var _ = Describe("UpdateVolume", func() { By("getting the corresponding ironcore volume") volume := &storagev1alpha1.Volume{} - volumeName := ironcoreMachine.Spec.Volumes[0].VolumeRef.Name + volumeName := ironcoreMachine.Spec.Volumes[1].VolumeRef.Name volumeKey := client.ObjectKey{Namespace: ns.Name, Name: volumeName} Expect(k8sClient.Get(ctx, volumeKey, volume)).To(Succeed()) @@ -67,7 +73,7 @@ var _ = Describe("UpdateVolume", func() { MachineId: machineID, Volume: &iri.Volume{ Name: "primary", - Device: "oda", + Device: "odb", Connection: &iri.VolumeConnection{ Driver: "test", Handle: "testhandle", @@ -90,9 +96,9 @@ var _ = Describe("UpdateVolume", func() { )) By("verifying machine volume is updated") - Eventually(Object(ironcoreMachine)).Should(HaveField("Spec.Volumes", ConsistOf(MatchFields(IgnoreExtras, Fields{ + Eventually(Object(ironcoreMachine)).Should(HaveField("Spec.Volumes", ContainElement(MatchFields(IgnoreExtras, Fields{ "Name": Equal("primary"), - "Device": Equal(ptr.To("oda")), + "Device": Equal(ptr.To("odb")), "VolumeSource": Equal(computev1alpha1.VolumeSource{ VolumeRef: &corev1.LocalObjectReference{Name: volume.Name}, }), diff --git a/client-go/applyconfigurations/compute/v1alpha1/localdiskvolumesource.go b/client-go/applyconfigurations/compute/v1alpha1/localdiskvolumesource.go new file mode 100644 index 000000000..698ac88f7 --- /dev/null +++ b/client-go/applyconfigurations/compute/v1alpha1/localdiskvolumesource.go @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" +) + +// LocalDiskVolumeSourceApplyConfiguration represents a declarative configuration of the LocalDiskVolumeSource type for use +// with apply. +type LocalDiskVolumeSourceApplyConfiguration struct { + SizeLimit *resource.Quantity `json:"sizeLimit,omitempty"` + Image *string `json:"image,omitempty"` +} + +// LocalDiskVolumeSourceApplyConfiguration constructs a declarative configuration of the LocalDiskVolumeSource type for use with +// apply. +func LocalDiskVolumeSource() *LocalDiskVolumeSourceApplyConfiguration { + return &LocalDiskVolumeSourceApplyConfiguration{} +} + +// WithSizeLimit sets the SizeLimit field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SizeLimit field is set to the value of the last call. +func (b *LocalDiskVolumeSourceApplyConfiguration) WithSizeLimit(value resource.Quantity) *LocalDiskVolumeSourceApplyConfiguration { + b.SizeLimit = &value + return b +} + +// WithImage sets the Image field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Image field is set to the value of the last call. +func (b *LocalDiskVolumeSourceApplyConfiguration) WithImage(value string) *LocalDiskVolumeSourceApplyConfiguration { + b.Image = &value + return b +} diff --git a/client-go/applyconfigurations/compute/v1alpha1/volume.go b/client-go/applyconfigurations/compute/v1alpha1/volume.go index 01a6aa691..ba13772e4 100644 --- a/client-go/applyconfigurations/compute/v1alpha1/volume.go +++ b/client-go/applyconfigurations/compute/v1alpha1/volume.go @@ -55,6 +55,14 @@ func (b *VolumeApplyConfiguration) WithEmptyDisk(value *EmptyDiskVolumeSourceApp return b } +// WithLocalDisk sets the LocalDisk field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LocalDisk field is set to the value of the last call. +func (b *VolumeApplyConfiguration) WithLocalDisk(value *LocalDiskVolumeSourceApplyConfiguration) *VolumeApplyConfiguration { + b.VolumeSourceApplyConfiguration.LocalDisk = value + return b +} + // WithEphemeral sets the Ephemeral field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Ephemeral field is set to the value of the last call. diff --git a/client-go/applyconfigurations/compute/v1alpha1/volumesource.go b/client-go/applyconfigurations/compute/v1alpha1/volumesource.go index bc4e6a58e..46ee0aee2 100644 --- a/client-go/applyconfigurations/compute/v1alpha1/volumesource.go +++ b/client-go/applyconfigurations/compute/v1alpha1/volumesource.go @@ -14,6 +14,7 @@ import ( type VolumeSourceApplyConfiguration struct { VolumeRef *v1.LocalObjectReference `json:"volumeRef,omitempty"` EmptyDisk *EmptyDiskVolumeSourceApplyConfiguration `json:"emptyDisk,omitempty"` + LocalDisk *LocalDiskVolumeSourceApplyConfiguration `json:"localDisk,omitempty"` Ephemeral *EphemeralVolumeSourceApplyConfiguration `json:"ephemeral,omitempty"` } @@ -39,6 +40,14 @@ func (b *VolumeSourceApplyConfiguration) WithEmptyDisk(value *EmptyDiskVolumeSou return b } +// WithLocalDisk sets the LocalDisk field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the LocalDisk field is set to the value of the last call. +func (b *VolumeSourceApplyConfiguration) WithLocalDisk(value *LocalDiskVolumeSourceApplyConfiguration) *VolumeSourceApplyConfiguration { + b.LocalDisk = value + return b +} + // WithEphemeral sets the Ephemeral field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Ephemeral field is set to the value of the last call. diff --git a/client-go/applyconfigurations/internal/internal.go b/client-go/applyconfigurations/internal/internal.go index 4633c2af5..caceb5113 100644 --- a/client-go/applyconfigurations/internal/internal.go +++ b/client-go/applyconfigurations/internal/internal.go @@ -121,6 +121,15 @@ var schemaYAML = typed.YAMLObject(`types: - name: volumeTemplate type: namedType: com.github.ironcore-dev.ironcore.api.storage.v1alpha1.VolumeTemplateSpec +- name: com.github.ironcore-dev.ironcore.api.compute.v1alpha1.LocalDiskVolumeSource + map: + fields: + - name: image + type: + scalar: string + - name: sizeLimit + type: + namedType: io.k8s.apimachinery.pkg.api.resource.Quantity - name: com.github.ironcore-dev.ironcore.api.compute.v1alpha1.Machine map: fields: @@ -401,6 +410,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: ephemeral type: namedType: com.github.ironcore-dev.ironcore.api.compute.v1alpha1.EphemeralVolumeSource + - name: localDisk + type: + namedType: com.github.ironcore-dev.ironcore.api.compute.v1alpha1.LocalDiskVolumeSource - name: name type: scalar: string diff --git a/client-go/applyconfigurations/utils.go b/client-go/applyconfigurations/utils.go index 6e1ecc577..6683f67a6 100644 --- a/client-go/applyconfigurations/utils.go +++ b/client-go/applyconfigurations/utils.go @@ -37,6 +37,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &computev1alpha1.EphemeralNetworkInterfaceSourceApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("EphemeralVolumeSource"): return &computev1alpha1.EphemeralVolumeSourceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("LocalDiskVolumeSource"): + return &computev1alpha1.LocalDiskVolumeSourceApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Machine"): return &computev1alpha1.MachineApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("MachineClass"): diff --git a/client-go/openapi/zz_generated.openapi.go b/client-go/openapi/zz_generated.openapi.go index eeff3bb00..aad1b066f 100644 --- a/client-go/openapi/zz_generated.openapi.go +++ b/client-go/openapi/zz_generated.openapi.go @@ -33,6 +33,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource": schema_ironcore_api_compute_v1alpha1_EmptyDiskVolumeSource(ref), "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EphemeralNetworkInterfaceSource": schema_ironcore_api_compute_v1alpha1_EphemeralNetworkInterfaceSource(ref), "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EphemeralVolumeSource": schema_ironcore_api_compute_v1alpha1_EphemeralVolumeSource(ref), + "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.LocalDiskVolumeSource": schema_ironcore_api_compute_v1alpha1_LocalDiskVolumeSource(ref), "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.Machine": schema_ironcore_api_compute_v1alpha1_Machine(ref), "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.MachineClass": schema_ironcore_api_compute_v1alpha1_MachineClass(ref), "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.MachineClassList": schema_ironcore_api_compute_v1alpha1_MachineClassList(ref), @@ -835,6 +836,34 @@ func schema_ironcore_api_compute_v1alpha1_EphemeralVolumeSource(ref common.Refer } } +func schema_ironcore_api_compute_v1alpha1_LocalDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LocalDiskVolumeSource is a volume that's offered by the machine pool provider. Usually ephemeral (i.e. deleted when the surrounding entity is deleted), with varying performance characteristics. Potentially not recoverable.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "sizeLimit": { + SchemaProps: spec.SchemaProps{ + Description: "SizeLimit is the total amount of local storage required for this LocalDisk volume. The default is nil which means that the limit is undefined.", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Image is the optional URL providing the operating system image of the machine.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + func schema_ironcore_api_compute_v1alpha1_Machine(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1446,7 +1475,7 @@ func schema_ironcore_api_compute_v1alpha1_MachineSpec(ref common.ReferenceCallba }, "image": { SchemaProps: spec.SchemaProps{ - Description: "Image is the optional URL providing the operating system image of the machine.", + Description: "Deprecated: Use LocalDisk to provide a bootable disk Image is the optional URL providing the operating system image of the machine.", Type: []string{"string"}, Format: "", }, @@ -1754,10 +1783,16 @@ func schema_ironcore_api_compute_v1alpha1_Volume(ref common.ReferenceCallback) c }, "emptyDisk": { SchemaProps: spec.SchemaProps{ - Description: "EmptyDisk instructs to use a Volume offered by the machine pool provider.", + Description: "Deprecated: Use LocalDisk instead EmptyDisk instructs to use a Volume offered by the machine pool provider.", Ref: ref("github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource"), }, }, + "localDisk": { + SchemaProps: spec.SchemaProps{ + Description: "LocalDisk instructs to use a Volume offered by the machine pool provider.", + Ref: ref("github.com/ironcore-dev/ironcore/api/compute/v1alpha1.LocalDiskVolumeSource"), + }, + }, "ephemeral": { SchemaProps: spec.SchemaProps{ Description: "Ephemeral instructs to create an ephemeral (i.e. coupled to the lifetime of the surrounding object) Volume to use.", @@ -1769,7 +1804,7 @@ func schema_ironcore_api_compute_v1alpha1_Volume(ref common.ReferenceCallback) c }, }, Dependencies: []string{ - "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource", "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EphemeralVolumeSource", "k8s.io/api/core/v1.LocalObjectReference"}, + "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource", "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EphemeralVolumeSource", "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.LocalDiskVolumeSource", "k8s.io/api/core/v1.LocalObjectReference"}, } } @@ -1788,10 +1823,16 @@ func schema_ironcore_api_compute_v1alpha1_VolumeSource(ref common.ReferenceCallb }, "emptyDisk": { SchemaProps: spec.SchemaProps{ - Description: "EmptyDisk instructs to use a Volume offered by the machine pool provider.", + Description: "Deprecated: Use LocalDisk instead EmptyDisk instructs to use a Volume offered by the machine pool provider.", Ref: ref("github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource"), }, }, + "localDisk": { + SchemaProps: spec.SchemaProps{ + Description: "LocalDisk instructs to use a Volume offered by the machine pool provider.", + Ref: ref("github.com/ironcore-dev/ironcore/api/compute/v1alpha1.LocalDiskVolumeSource"), + }, + }, "ephemeral": { SchemaProps: spec.SchemaProps{ Description: "Ephemeral instructs to create an ephemeral (i.e. coupled to the lifetime of the surrounding object) Volume to use.", @@ -1802,7 +1843,7 @@ func schema_ironcore_api_compute_v1alpha1_VolumeSource(ref common.ReferenceCallb }, }, Dependencies: []string{ - "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource", "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EphemeralVolumeSource", "k8s.io/api/core/v1.LocalObjectReference"}, + "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EmptyDiskVolumeSource", "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.EphemeralVolumeSource", "github.com/ironcore-dev/ironcore/api/compute/v1alpha1.LocalDiskVolumeSource", "k8s.io/api/core/v1.LocalObjectReference"}, } } diff --git a/docs/api-reference/compute.md b/docs/api-reference/compute.md index c22466e21..02538ce54 100644 --- a/docs/api-reference/compute.md +++ b/docs/api-reference/compute.md @@ -134,7 +134,8 @@ string
Image is the optional URL providing the operating system image of the machine.
+Deprecated: Use LocalDisk to provide a bootable disk +Image is the optional URL providing the operating system image of the machine.
+(Appears on:VolumeSource) +
+LocalDiskVolumeSource is a volume that’s offered by the machine pool provider. +Usually ephemeral (i.e. deleted when the surrounding entity is deleted), with +varying performance characteristics. Potentially not recoverable.
+| Field | +Description | +
|---|---|
+sizeLimit+ + +k8s.io/apimachinery/pkg/api/resource.Quantity + + + |
+
+ SizeLimit is the total amount of local storage required for this LocalDisk volume. +The default is nil which means that the limit is undefined. + |
+
+image+ +string + + |
+
+(Optional)
+ Image is the optional URL providing the operating system image of the machine. + |
+
Image is the optional URL providing the operating system image of the machine.
+Deprecated: Use LocalDisk to provide a bootable disk +Image is the optional URL providing the operating system image of the machine.
EmptyDisk instructs to use a Volume offered by the machine pool provider.
+Deprecated: Use LocalDisk instead +EmptyDisk instructs to use a Volume offered by the machine pool provider.
+localDiskLocalDisk instructs to use a Volume offered by the machine pool provider.