Skip to content

Commit f1dc06e

Browse files
authored
Merge pull request #633 from eshulman2/vol
Add imageRef to volume controller
2 parents 47af88c + 0801402 commit f1dc06e

20 files changed

Lines changed: 189 additions & 2 deletions

api/v1alpha1/volume_types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ type VolumeResourceSpec struct {
5656
// +listType=atomic
5757
// +optional
5858
Metadata []VolumeMetadata `json:"metadata,omitempty"`
59+
60+
// imageRef is a reference to an ORC Image. If specified, creates a
61+
// bootable volume from this image. The volume size must be >= the
62+
// image's min_disk requirement.
63+
// +optional
64+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="imageRef is immutable"
65+
ImageRef *KubernetesNameRef `json:"imageRef,omitempty"`
5966
}
6067

6168
// VolumeFilter defines an existing resource by its properties
@@ -176,6 +183,11 @@ type VolumeResourceStatus struct {
176183
// +optional
177184
Bootable *bool `json:"bootable,omitempty"`
178185

186+
// imageID is the ID of the image this volume was created from, if any.
187+
// +kubebuilder:validation:MaxLength=1024
188+
// +optional
189+
ImageID string `json:"imageID,omitempty"`
190+
179191
// encrypted denotes if the volume is encrypted.
180192
// +optional
181193
Encrypted *bool `json:"encrypted,omitempty"`

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/models-schema/zz_generated.openapi.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/openstack.k-orc.cloud_volumes.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,17 @@ spec:
173173
maxLength: 255
174174
minLength: 1
175175
type: string
176+
imageRef:
177+
description: |-
178+
imageRef is a reference to an ORC Image. If specified, creates a
179+
bootable volume from this image. The volume size must be >= the
180+
image's min_disk requirement.
181+
maxLength: 253
182+
minLength: 1
183+
type: string
184+
x-kubernetes-validations:
185+
- message: imageRef is immutable
186+
rule: self == oldSelf
176187
metadata:
177188
description: |-
178189
metadata key and value pairs to be associated with the volume.
@@ -389,6 +400,11 @@ spec:
389400
description: host is the identifier of the host holding the volume.
390401
maxLength: 1024
391402
type: string
403+
imageID:
404+
description: imageID is the ID of the image this volume was created
405+
from, if any.
406+
maxLength: 1024
407+
type: string
392408
metadata:
393409
description: metadata key and value pairs to be associated with
394410
the volume.

config/samples/openstack_v1alpha1_volume.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ spec:
1212
description: Sample Volume
1313
size: 100
1414
volumeTypeRef: my-volume-type
15+
imageRef: ubuntu-2404
1516
metadata:
1617
key1: value1
1718
key2: value2

internal/controllers/volume/actuator.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress"
3333
"github.com/k-orc/openstack-resource-controller/v2/internal/logging"
3434
"github.com/k-orc/openstack-resource-controller/v2/internal/osclients"
35+
"github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency"
3536
orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors"
3637
)
3738

@@ -165,6 +166,17 @@ func (actuator volumeActuator) CreateResource(ctx context.Context, obj orcObject
165166
}
166167
}
167168

169+
// Resolve image dependency for bootable volumes
170+
image, imageDepRS := dependency.FetchDependency(
171+
ctx, actuator.k8sClient, obj.Namespace,
172+
resource.ImageRef, "Image",
173+
func(dep *orcv1alpha1.Image) bool {
174+
return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil
175+
},
176+
)
177+
reconcileStatus = reconcileStatus.WithReconcileStatus(imageDepRS)
178+
imageID := ptr.Deref(image.Status.ID, "")
179+
168180
if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule {
169181
return nil, reconcileStatus
170182
}
@@ -181,6 +193,7 @@ func (actuator volumeActuator) CreateResource(ctx context.Context, obj orcObject
181193
Metadata: metadata,
182194
VolumeType: volumetypeID,
183195
AvailabilityZone: resource.AvailabilityZone,
196+
ImageID: imageID,
184197
}
185198

186199
osResource, err := actuator.osClient.CreateVolume(ctx, createOpts)

internal/controllers/volume/controller.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ var volumetypeDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.Vo
7474
finalizer, externalObjectFieldOwner,
7575
)
7676

77+
// No deletion guard for image, because images can be safely deleted while
78+
// referenced by a volume
79+
var imageDependency = dependency.NewDependency[*orcv1alpha1.VolumeList, *orcv1alpha1.Image](
80+
"spec.resource.imageRef",
81+
func(volume *orcv1alpha1.Volume) []string {
82+
resource := volume.Spec.Resource
83+
if resource == nil || resource.ImageRef == nil {
84+
return nil
85+
}
86+
return []string{string(*resource.ImageRef)}
87+
},
88+
)
89+
7790
// serverToVolumeMapFunc creates a mapping function that reconciles volumes when:
7891
// - a volume ID appears in server status but the volume doesn't have attachment info for that server
7992
// - a volume has attachment info for a server, but the server no longer lists that volume
@@ -209,18 +222,27 @@ func (c volumeReconcilerConstructor) SetupWithManager(ctx context.Context, mgr c
209222
return err
210223
}
211224

225+
imageWatchEventHandler, err := imageDependency.WatchEventHandler(log, k8sClient)
226+
if err != nil {
227+
return err
228+
}
229+
212230
builder := ctrl.NewControllerManagedBy(mgr).
213231
WithOptions(options).
214232
Watches(&orcv1alpha1.VolumeType{}, volumetypeWatchEventHandler,
215233
builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.VolumeType{})),
216234
).
235+
Watches(&orcv1alpha1.Image{}, imageWatchEventHandler,
236+
builder.WithPredicates(predicates.NewBecameAvailable(log, &orcv1alpha1.Image{})),
237+
).
217238
Watches(&orcv1alpha1.Server{}, handler.EnqueueRequestsFromMapFunc(serverToVolumeMapFunc(ctx, k8sClient)),
218239
builder.WithPredicates(predicates.NewServerVolumesChanged(log)),
219240
).
220241
For(&orcv1alpha1.Volume{})
221242

222243
if err := errors.Join(
223244
volumetypeDependency.AddToManager(ctx, mgr),
245+
imageDependency.AddToManager(ctx, mgr),
224246
credentialsDependency.AddToManager(ctx, mgr),
225247
credentials.AddCredentialsWatch(log, mgr.GetClient(), builder, credentialsDependency),
226248
); err != nil {

internal/controllers/volume/status.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ func (volumeStatusWriter) ApplyResourceStatus(log logr.Logger, osResource *osRes
9292
}
9393
}
9494

95+
// Extract image ID from volume_image_metadata if present.
96+
// When a volume is created from an image, OpenStack stores the source
97+
// image ID in the volume's metadata under "image_id".
98+
if imageID, ok := osResource.VolumeImageMetadata["image_id"]; ok {
99+
resourceStatus.WithImageID(imageID)
100+
}
101+
95102
for k, v := range osResource.Metadata {
96103
resourceStatus.WithMetadata(orcapplyconfigv1alpha1.VolumeMetadataStatus().
97104
WithName(k).

internal/controllers/volume/tests/volume-dependency/00-assert.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,18 @@ status:
2828
message: Waiting for VolumeType/volume-dependency to be created
2929
status: "True"
3030
reason: Progressing
31+
---
32+
apiVersion: openstack.k-orc.cloud/v1alpha1
33+
kind: Volume
34+
metadata:
35+
name: volume-dependency-no-image
36+
status:
37+
conditions:
38+
- type: Available
39+
message: Waiting for Image/volume-dependency-image to be created
40+
status: "False"
41+
reason: Progressing
42+
- type: Progressing
43+
message: Waiting for Image/volume-dependency-image to be created
44+
status: "True"
45+
reason: Progressing

internal/controllers/volume/tests/volume-dependency/00-create-resources-missing-deps.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@ spec:
2323
managementPolicy: managed
2424
resource:
2525
size: 1
26+
---
27+
apiVersion: openstack.k-orc.cloud/v1alpha1
28+
kind: Volume
29+
metadata:
30+
name: volume-dependency-no-image
31+
spec:
32+
cloudCredentialsRef:
33+
cloudName: openstack
34+
secretName: openstack-clouds
35+
managementPolicy: managed
36+
resource:
37+
size: 1
38+
imageRef: volume-dependency-image

0 commit comments

Comments
 (0)