Skip to content

feat(instanceV2) - Add imageName and ImageNameRef to the blockDevice … - #177

Open
JRaver wants to merge 1 commit into
crossplane-contrib:mainfrom
JRaver:feature/add-image-name-ref
Open

JRaver wants to merge 1 commit into
crossplane-contrib:mainfrom
JRaver:feature/add-image-name-ref

Conversation

@JRaver

@JRaver JRaver commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

PR: Add imageName to blockDevice for InstanceV2

Description of your changes

Adds imageName / imageNameRef / imageNameSelector support to blockDevice[] entries of InstanceV2 (both cluster and namespaced scope).

Background

The upstream Terraform provider resolves image_name → image UUID only for the top-level image_name field (ephemeral-disk boot). For boot-from-volume (block_device[].uuid, source_type: image, destination_type: volume) the provider requires a raw image UUID. This made it impossible to write portable manifests across clusters where the same image has different UUIDs.

What this PR does

New field blockDevice[].imageName

  • Resolved to an image UUID at create-time via ImageV2Client + imagesutils.IDFromName (Glance API), without any changes to the upstream Terraform provider.
  • Mutually exclusive with uuid; only valid for source_type: image.
  • ForceNew: true — changing the name is detected as drift and blocks the update (refuse to update … requires replacing it), consistent with the existing uuid behaviour.

Drift detection via ReadContext wrapper

  • After each reconcile Read, the provider looks up the current name of the boot-volume image (by UUID) in Glance and writes it back to the Terraform state as image_name.
  • On the next reconcile, CustomizeDiff compares the config name with the observed name. A change produces a ForceNew diff that upjet refuses to apply, surfacing a clear error.
  • Lookup failures (deleted / inaccessible image) are silently ignored so they never cause a false drift.

imageNameRef / imageNameSelector — optional; resolves the name from a ImageV2 managed resource using ExtractParamPath("name", false). Works with both fully-managed and managementPolicies: [Observe] images (for the latter, set spec.forProvider.name explicitly).

Late-initialize guard — BlockDevice.ImageName is added to LateInitializer.IgnoredFields so it is never overwritten by the observed UUID during late-init.

Usage examples

Direct name (no ImageV2 object required):

apiVersion: compute.openstack.crossplane.io/v1alpha1
kind: InstanceV2
metadata:
  name: my-vm
spec:
  forProvider:
    blockDevice:
    - bootIndex: 0
      sourceType: image
      destinationType: volume
      imageName: Ubuntu_2404_openstack
      volumeSize: 30
      volumeType: __DEFAULT__
      deleteOnTermination: true
    flavorName: vm.c4r8

With reference to an observe-only ImageV2:

# On the cluster that owns the image:
apiVersion: images.openstack.crossplane.io/v1alpha1
kind: ImageV2
metadata:
  annotations:
    crossplane.io/external-name: 36ad9881-eb9c-47f3-9d82-be9327998288
  name: ubuntu-2404
spec:
  forProvider:
    name: Ubuntu_2404_openstack   # required for imageNameRef resolution
  managementPolicies: [Observe]
  providerConfigRef:
    name: default
---
apiVersion: compute.openstack.crossplane.io/v1alpha1
kind: InstanceV2
metadata:
  name: my-vm
spec:
  forProvider:
    blockDevice:
    - bootIndex: 0
      sourceType: image
      destinationType: volume
      imageNameRef:
        name: ubuntu-2404
      volumeSize: 30
      deleteOnTermination: true

I have

  • Read and followed Crossplane's contribution process.
  • Run make submodules; make reviewable test to ensure this PR is ready for review.

How has this code been tested

  • go test ./config/common/blockdevice/... — unit tests covering:
    • schema extension (image_name field present, DiffSuppressFunc set on uuid)
    • CreateContext wrapper: name resolved to UUID before upstream create
    • validation errors: uuid + imageName conflict, wrong source_type, image not found, multiple images with same name
    • ReadContext wrapper: image name populated from Glance after read
    • lookup failure during read does not fail reconciliation and preserves previous state
    • diff table: new resource, same name (no diff), changed name (ForceNew diff), no observed name (no diff), uuid-only config, changed uuid
  • make generate — verified generated types (ImageName, ImageNameRef, ImageNameSelector in blockDevice for both cluster and namespaced scope), resolvers, deepcopy, CRDs
  • make reviewable test — 0 issues, all unit tests pass
  • Live cluster (OpenStack):
    • InstanceV2 with imageName: Ubuntu_2404_openstack created successfully; status.atProvider.blockDevice[0].imageName shows the resolved name after first reconcile
    • Changing imageName to a different image name → Synced=False, error refuse to update … cannot change the value of the argument "block_device.0.image_name" — server untouched
    • imageNameRef pointing to an observe-only ImageV2 with forProvider.name set — resolves correctly
    • Non-existent image name → create-time error from Glance before any Nova call

…sections

Signed-off-by: Yevhen Artemenko <eartemenko@playtika.com>
@JRaver
JRaver requested a review from Avatos as a code owner October 15, 2026 09:11
@JRaver

JRaver commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

@Avatos Please check the PR when you have time, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant