Skip to content

Commit 2957c0e

Browse files
authored
Add architecture hints to ephemeral volumes (#1435)
1 parent 958d352 commit 2957c0e

File tree

4 files changed

+116
-3
lines changed

4 files changed

+116
-3
lines changed

api/common/v1alpha1/common_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ const (
3232
// some ephemeral controller is managing the resource.
3333
EphemeralManagedByAnnotation = "common.ironcore.dev/ephemeral-managed-by"
3434

35+
// MachineArchitectureLabel is a label that indicates the machine architecture.
36+
MachineArchitectureLabel = "common.ironcore.dev/architecture"
37+
3538
// DefaultEphemeralManager is the default ironcoreephemeral manager.
3639
DefaultEphemeralManager = "ephemeral-manager"
3740
)

internal/controllers/compute/machine_ephemeralvolume_controller.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ import (
99
"fmt"
1010
"maps"
1111

12-
"github.com/ironcore-dev/ironcore/poollet/machinepoollet/controllers/events"
13-
"github.com/ironcore-dev/ironcore/utils/annotations"
14-
1512
"github.com/go-logr/logr"
13+
commonv1alpha1 "github.com/ironcore-dev/ironcore/api/common/v1alpha1"
1614
computev1alpha1 "github.com/ironcore-dev/ironcore/api/compute/v1alpha1"
1715
storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1"
1816
computeclient "github.com/ironcore-dev/ironcore/internal/client/compute"
17+
"github.com/ironcore-dev/ironcore/poollet/machinepoollet/controllers/events"
18+
"github.com/ironcore-dev/ironcore/utils/annotations"
1919
corev1 "k8s.io/api/core/v1"
2020
apierrors "k8s.io/apimachinery/pkg/api/errors"
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"k8s.io/apimachinery/pkg/types"
2223
"k8s.io/client-go/tools/record"
2324
"k8s.io/klog/v2"
2425
ctrl "sigs.k8s.io/controller-runtime"
@@ -130,6 +131,34 @@ func (r *MachineEphemeralVolumeReconciler) handleCreateVolume(ctx context.Contex
130131
return r.handleExistingVolume(ctx, log, machine, true, volume)
131132
}
132133

134+
func (r *MachineEphemeralVolumeReconciler) getMachineArchitecture(ctx context.Context, machine *computev1alpha1.Machine) (*string, error) {
135+
machineClass := &computev1alpha1.MachineClass{}
136+
if err := r.Get(ctx, types.NamespacedName{Name: machine.Spec.MachineClassRef.Name}, machineClass); err != nil {
137+
return nil, fmt.Errorf("error getting machine class %s: %w", machine.Spec.MachineClassRef.Name, err)
138+
}
139+
140+
architecture, ok := machineClass.Labels[commonv1alpha1.MachineArchitectureLabel]
141+
if !ok {
142+
return nil, nil
143+
}
144+
145+
return &architecture, nil
146+
}
147+
148+
func (r *MachineEphemeralVolumeReconciler) addArchitectureIfNeeded(log logr.Logger, volume *storagev1alpha1.Volume, architecture *string) {
149+
if volume.Spec.DataSource.OSImage == nil || architecture == nil {
150+
return
151+
}
152+
153+
if volume.Spec.DataSource.OSImage.Architecture != nil {
154+
log.V(2).Info("Architecture already set", "architecture", volume.Spec.DataSource.OSImage.Architecture)
155+
return
156+
}
157+
158+
log.V(2).Info("Adding architecture", "architecture", architecture)
159+
volume.Spec.DataSource.OSImage.Architecture = architecture
160+
}
161+
133162
func (r *MachineEphemeralVolumeReconciler) reconcile(ctx context.Context, log logr.Logger, machine *computev1alpha1.Machine) (ctrl.Result, error) {
134163
log.V(1).Info("Reconcile")
135164

@@ -141,6 +170,11 @@ func (r *MachineEphemeralVolumeReconciler) reconcile(ctx context.Context, log lo
141170
return ctrl.Result{}, fmt.Errorf("error listing volumes: %w", err)
142171
}
143172

173+
arch, err := r.getMachineArchitecture(ctx, machine)
174+
if err != nil {
175+
return ctrl.Result{}, fmt.Errorf("error getting machine architecture: %w", err)
176+
}
177+
144178
var (
145179
ephemVolumeByName = r.ephemeralMachineVolumeByName(machine)
146180
errs []error
@@ -157,6 +191,7 @@ func (r *MachineEphemeralVolumeReconciler) reconcile(ctx context.Context, log lo
157191

158192
for _, volume := range ephemVolumeByName {
159193
log := log.WithValues("Volume", klog.KObj(volume))
194+
r.addArchitectureIfNeeded(log, volume, arch)
160195
if err := r.handleCreateVolume(ctx, log, machine, volume); err != nil {
161196
if apierrors.IsForbidden(err) {
162197
r.Eventf(machine, corev1.EventTypeNormal, events.VolumeNotReady, "Volume %s exceeded quota ", volume.Name)

internal/controllers/compute/machine_ephemeralvolume_controller_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@ package compute
55

66
import (
77
computev1alpha1 "github.com/ironcore-dev/ironcore/api/compute/v1alpha1"
8+
corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1"
89
storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1"
910
"github.com/ironcore-dev/ironcore/utils/annotations"
1011
. "github.com/ironcore-dev/ironcore/utils/testing"
1112
. "github.com/onsi/ginkgo/v2"
1213
. "github.com/onsi/gomega"
1314
corev1 "k8s.io/api/core/v1"
1415
apierrors "k8s.io/apimachinery/pkg/api/errors"
16+
"k8s.io/apimachinery/pkg/api/resource"
1517
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/utils/ptr"
1619
ctrl "sigs.k8s.io/controller-runtime"
1720
. "sigs.k8s.io/controller-runtime/pkg/envtest/komega"
1821
)
1922

2023
var _ = Describe("MachineEphemeralVolumeController", func() {
2124
ns := SetupNamespace(&k8sClient)
2225
machineClass := SetupMachineClass()
26+
volumeClass := SetupVolumeClass()
2327

2428
It("should manage ephemeral volumes for a machine", func(ctx SpecContext) {
2529
By("creating a volume that will be referenced by the machine")
@@ -90,6 +94,58 @@ var _ = Describe("MachineEphemeralVolumeController", func() {
9094
Eventually(Get(undesiredControlledVolume)).Should(Satisfy(apierrors.IsNotFound))
9195
})
9296

97+
It("should manage ephemeral volumes for a machine and setting architecture hint", func(ctx SpecContext) {
98+
By("creating a machine")
99+
machine := &computev1alpha1.Machine{
100+
ObjectMeta: metav1.ObjectMeta{
101+
Namespace: ns.Name,
102+
GenerateName: "machine-",
103+
},
104+
Spec: computev1alpha1.MachineSpec{
105+
MachineClassRef: corev1.LocalObjectReference{Name: machineClass.Name},
106+
Volumes: []computev1alpha1.Volume{
107+
{
108+
Name: "ephem-volume",
109+
VolumeSource: computev1alpha1.VolumeSource{
110+
Ephemeral: &computev1alpha1.EphemeralVolumeSource{
111+
VolumeTemplate: &storagev1alpha1.VolumeTemplateSpec{
112+
Spec: storagev1alpha1.VolumeSpec{
113+
VolumeClassRef: &corev1.LocalObjectReference{Name: volumeClass.Name},
114+
Resources: corev1alpha1.ResourceList{
115+
corev1alpha1.ResourceStorage: resource.MustParse("500Mi"),
116+
},
117+
DataSource: storagev1alpha1.VolumeDataSource{
118+
OSImage: &storagev1alpha1.OSDataSource{
119+
Image: "test-os-image",
120+
},
121+
},
122+
},
123+
},
124+
},
125+
},
126+
},
127+
},
128+
},
129+
}
130+
Expect(k8sClient.Create(ctx, machine)).To(Succeed())
131+
132+
By("waiting for the ephemeral volume to exist")
133+
ephemVolume := &storagev1alpha1.Volume{
134+
ObjectMeta: metav1.ObjectMeta{
135+
Namespace: ns.Name,
136+
Name: computev1alpha1.MachineEphemeralVolumeName(machine.Name, "ephem-volume"),
137+
},
138+
}
139+
Eventually(Get(ephemVolume)).Should(Succeed())
140+
141+
Eventually(Object(ephemVolume)).Should(SatisfyAll(
142+
HaveField("Spec.DataSource.OSImage", &storagev1alpha1.OSDataSource{
143+
Image: "test-os-image",
144+
Architecture: ptr.To("amd64"),
145+
}),
146+
))
147+
})
148+
93149
It("should not delete externally managed volumes for a machine", func(ctx SpecContext) {
94150
By("creating a machine")
95151
machine := &computev1alpha1.Machine{

internal/controllers/compute/suite_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"testing"
1111
"time"
1212

13+
commonv1alpha1 "github.com/ironcore-dev/ironcore/api/common/v1alpha1"
14+
1315
"github.com/ironcore-dev/controller-utils/buildutils"
1416
computev1alpha1 "github.com/ironcore-dev/ironcore/api/compute/v1alpha1"
1517
corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1"
@@ -177,6 +179,9 @@ func SetupMachineClass() *computev1alpha1.MachineClass {
177179
*machineClass = computev1alpha1.MachineClass{
178180
ObjectMeta: metav1.ObjectMeta{
179181
GenerateName: "machine-class-",
182+
Labels: map[string]string{
183+
commonv1alpha1.MachineArchitectureLabel: "amd64",
184+
},
180185
},
181186
Capabilities: corev1alpha1.ResourceList{
182187
corev1alpha1.ResourceCPU: resource.MustParse("1"),
@@ -185,3 +190,17 @@ func SetupMachineClass() *computev1alpha1.MachineClass {
185190
}
186191
})
187192
}
193+
194+
func SetupVolumeClass() *storagev1alpha1.VolumeClass {
195+
return SetupObjectStruct[*storagev1alpha1.VolumeClass](&k8sClient, func(volumeClass *storagev1alpha1.VolumeClass) {
196+
*volumeClass = storagev1alpha1.VolumeClass{
197+
ObjectMeta: metav1.ObjectMeta{
198+
GenerateName: "volume-class-",
199+
},
200+
Capabilities: corev1alpha1.ResourceList{
201+
corev1alpha1.ResourceIOPS: resource.MustParse("500"),
202+
corev1alpha1.ResourceTPS: resource.MustParse("500Mi"),
203+
},
204+
}
205+
})
206+
}

0 commit comments

Comments
 (0)