Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migration Guide: v1beta1 to v1beta2

This guide covers the breaking changes introduced when migrating from Cluster API Provider Metal3 (CAPM3) v1beta1 to v1beta2 API.

Overview

The v1beta2 API introduces several breaking changes including field renames, type changes, and structural modifications. This guide will help you update your manifests and configurations.

The primary driver for these changes is the migration of conditions from Cluster API’s clusterv1beta1.Conditions to Kubernetes native metav1.Condition to align with standard Kubernetes patterns. Additionally, the kube-api-linter was adopted in v1beta2 to enforce Kubernetes API conventions and best practices, resulting in many of the field renames, type changes, and structural improvements documented below.

Breaking Changes

Metal3Machine

Field Renames

The JSON field name for DiskFormat in spec.image has been renamed:

v1beta1 Field Namev1beta2 Field Name
formatdiskFormat

Example migration:

# v1beta1
spec:
  image:
    url: http://example.com/image.qcow2
    checksum: http://example.com/image.qcow2.sha256
    format: qcow2
# v1beta2
spec:
  image:
    url: http://example.com/image.qcow2
    checksum: http://example.com/image.qcow2.sha256
    diskFormat: qcow2

NoCloudProvider Removed

The deprecated NoCloudProvider field has been removed from the v1beta2 API.

Migration:

  • Replace all usages of NoCloudProvider with CloudProviderEnabled
  • Note that the logic is inverted: to keep the same behavior, change NoCloudProvider: false to CloudProviderEnabled: true

Example migration:

# v1beta1
spec:
  noCloudProvider: true
# v1beta2
spec:
  cloudProviderEnabled: false
# v1beta1
spec:
  noCloudProvider: false
# v1beta2
spec:
  cloudProviderEnabled: true

Metal3Machine Type Changes (Pointer vs Value)

FieldType Change
spec.hostSelectorHostSelector → *HostSelector
spec.automatedCleaningMode*string → string

Metal3Machine Fields with Added omitzero

Field
spec.image
spec.customDeploy

Metal3Machine Status Changes

v1beta1 Fieldv1beta2 FieldNotes
status.readystatus.initialization.provisionedBool → *bool pointer
status.phaseRemovedNo longer available
status.conditionsstatus.conditionsType changed (see Conditions section)
status.failureReasonstatus.deprecated.v1beta1.failureReasonMoved to deprecated, no longer terminal
status.failureMessagestatus.deprecated.v1beta1.failureMessageMoved to deprecated, no longer terminal

Note: In v1beta1, failureReason and failureMessage represented terminal errors that required manual intervention. In v1beta2, these fields are deprecated and moved to status.deprecated.v1beta1. They no longer represent terminal errors - error information should now be obtained from the status.conditions field.

Example v1beta2 Metal3Machine status structure:

status:
  initialization:
    provisioned: true
  conditions:
  - type: Ready
    status: "True"
    reason: Ready
  - type: AssociateBareMetalHost
    status: "True"
    reason: AssociateBareMetalHostSuccess
  - type: AssociateMetal3MachineMetaData
    status: "True"
    reason: AssociateMetal3MachineMetaDataSuccess
  - type: Metal3DataReady
    status: "True"
    reason: Metal3DataSecretsReady
  deprecated:
    v1beta1:
      conditions:  # Legacy v1beta1 conditions for backwards compatibility
      - type: AssociateBMH
        status: "True"
        severity: Info
        reason: AssociateBMHSuccess
        lastTransitionTime: "2026-01-01T00:00:00Z"
      - type: KubernetesNodeReady
        status: "True"
        severity: Info
        reason: KubernetesNodeReady
        lastTransitionTime: "2026-01-01T00:00:00Z"
      - type: Metal3DataReady
        status: "True"
        severity: Info
        reason: Metal3DataReady
        lastTransitionTime: "2026-01-01T00:00:00Z"
      failureReason: null
      failureMessage: null

Metal3Machine Condition Type Name Changes

v1beta1 Condition Typev1beta2 Condition Type
AssociateBMHAssociateBareMetalHost
KubernetesNodeReadyAssociateMetal3MachineMetaData
Metal3DataReadyMetal3DataReady (unchanged)

Metal3Machine Condition Reason Name Changes

v1beta1 Reasonv1beta2 Reason
WaitingForClusterInfrastructureWaitingForClusterInfrastructureReady
WaitingForBootstrapReadyWaitingForBootstrapData
AssociateBMHFailedAssociateBareMetalHostFailed
PauseAnnotationRemoveFailedBareMetalHostPauseAnnotationRemoveFailed
PauseAnnotationSetFailedReasonBareMetalHostPauseAnnotationSetFailed
AssociateM3MetaDataFailedAssociateMetal3MachineMetaDataFailed
N/AAssociateBareMetalHostSuccess (new)
N/AAssociateBareMetalHostViaNodeReuseSuccess (new)
N/AAssociateMetal3MachineMetaDataSuccess (new)
N/AMetal3DataSecretsReady (new)
N/ASecretsSetExternally (new)

Metal3MachineTemplate

Metal3MachineTemplate Type Changes (Pointer vs Value)

FieldType Change
spec.nodeReusebool → *bool

Metal3Cluster

Metal3Cluster Status Changes

v1beta1 Fieldv1beta2 FieldNotes
status.readystatus.initialization.provisionedBool → *bool pointer
status.conditionsstatus.conditionsType changed (see Conditions section)
status.failureReasonstatus.deprecated.v1beta1.failureReasonMoved to deprecated, no longer terminal
status.failureMessagestatus.deprecated.v1beta1.failureMessageMoved to deprecated, no longer terminal

Example v1beta2 Metal3Cluster status structure:

status:
  initialization:
    provisioned: true
  conditions:
  - type: Ready
    status: "True"
    reason: Ready
  - type: BaremetalInfrastructureReady
    status: "True"
    reason: Ready
  deprecated:
    v1beta1:
      conditions:  # Legacy v1beta1 conditions for backwards compatibility
      - type: BaremetalInfrastructureReady
        status: "True"
        severity: Info
        reason: Ready
        lastTransitionTime: "2026-01-01T00:00:00Z"
      failureReason: null
      failureMessage: null

Metal3Cluster Condition Type Name Changes

v1beta1 Condition Typev1beta2 Condition Type
BaremetalInfrastructureReadyBaremetalInfrastructureReady (unchanged)

Metal3DataTemplate

Metal3DataTemplate Field Renames

The following JSON field names in spec.metaData have been renamed:

v1beta1 Field Namev1beta2 Field Name
ipAddressesFromIPPoolipAddressesFromPool
prefixesFromIPPoolprefixesFromPool
gatewaysFromIPPoolgatewaysFromPool
dnsServersFromIPPooldnsServersFromPool

Example migration:

# v1beta1
spec:
  metaData:
    ipAddressesFromIPPool:
    - key: node_ip
      name: pool-1
# v1beta2
spec:
  metaData:
    ipAddressesFromPool:
    - key: node_ip
      name: pool-1

Metal3DataTemplate Type Changes (int to int32)

The following JSON field types have been changed from int to int32:

Field PathType Change
spec.metaData.index.offsetint → int32
spec.metaData.index.stepint → int32
spec.metaData.ipaddress.stepint → int32
spec.networkData.links.ethernet.mtuint → int32
spec.networkData.links.bond.mtuint → int32
spec.networkData.links.vlan.vlanIDint → int32
spec.networkData.links.vlan.mtuint → int32
spec.networkData.routes.ipv4.prefixint → int32
spec.networkData.routes.ipv6.prefixint → int32

These type changes should be transparent for most users as the YAML representation remains the same.

Metal3DataTemplate Type Changes (Pointer vs Value)

Value to Pointer:

FieldType Change
spec.metaData.index.offsetint32 → *int32
spec.metaData.fromHostInterfaces[].fromBootMACbool → *bool
spec.networkData.routes.ipv4[].prefixint32 → *int32
spec.networkData.routes.ipv4[].servicesNetworkDataServicev4 → *NetworkDataServicev4
spec.networkData.routes.ipv6[].prefixint32 → *int32
spec.networkData.routes.ipv6[].servicesNetworkDataServicev6 → *NetworkDataServicev6
spec.networkData.linksNetworkDataLink → *NetworkDataLink
spec.networkData.networksNetworkDataNetwork → *NetworkDataNetwork
spec.networkData.servicesNetworkDataService → *NetworkDataService

Pointer to Value:

FieldType Change
spec.networkData.services.dnsFromIPPool*string → string
spec.networkData.services.dns[].dnsFromIPPool*string → string

Metal3DataTemplate Fields with Added omitzero

Field
spec
spec.networkData.links.ethernets[].macAddress.fromAnnotation
spec.networkData.networks.ipv4[].fromPoolAnnotation
spec.networkData.networks.ipv6[].fromPoolAnnotation
spec.networkData.networks.ipv4DHCP[].fromPoolAnnotation
spec.networkData.networks.ipv6DHCP[].fromPoolAnnotation

Map to Array Conversions

The following fields have been converted from untyped maps to structured arrays:

NetworkDataLinkBond.parameters

v1beta1 structure:

"parameters": {
  "key1": "value1",
  "key2": "value2"
}

v1beta2 structure:

"parameters": [
  {
    "name": "key1",
    "value": "value1"
  },
  {
    "name": "key2",
    "value": "value2"
  }
]

YAML migration example:

# v1beta1
spec:
  networkData:
    links:
      bonds:
      - id: bond0
        parameters:
          mode: "802.3ad"
          xmit_hash_policy: "layer3+4"
# v1beta2
spec:
  networkData:
    links:
      bonds:
      - id: bond0
        parameters:
        - name: mode
          value: "802.3ad"
        - name: xmit_hash_policy
          value: "layer3+4"
status.indexes

v1beta1 structure:

"indexes": {
  "machine1": 0,
  "machine2": 1
}

v1beta2 structure:

"indexes": [
  {
    "name": "machine1",
    "index": 0
  },
  {
    "name": "machine2",
    "index": 1
  }
]

Metal3DataTemplate TemplateReference Removed

The TemplateReference field was removed from Metal3DataTemplate.

Migration: Remove all usage of the TemplateReference field from your manifests.

New Types

IndexEntry - Used in Metal3DataTemplate.status.indexes:

Name  string // required
Index int32  // required

NetworkDataLinkBondParam - Used in Metal3DataTemplate.spec.networkData.links.bond.parameters:

Name  string                   // required
Value apiextensionsv1.JSON     // optional

Metal3Data

Metal3Data Type Changes (Pointer vs Value)

Value to Pointer:

FieldType Change
spec.indexint32 → *int32
status.readybool → *bool

Pointer to Value:

FieldType Change
status.errorMessage*string → string

Metal3Data Fields with Added omitzero

Field
spec

Metal3Data TemplateReference Removed

The TemplateReference field was removed from Metal3Data.

Migration: Remove all usage of the TemplateReference field from your manifests.

Metal3Remediation

Metal3Remediation Type Changes (int to int32)

The following JSON field types have been changed from int to int32:

Field PathType Change
spec.strategy.retryLimitint → int32
status.retryCountint → int32

Timeout Field Change

The timeout field in RemediationStrategy has been replaced with timeoutSeconds:

v1beta1 Fieldv1beta2 Field
timeout (*metav1.Duration)timeoutSeconds (int32)
  • v1beta1 timeout field: accepts duration strings (e.g., "600s", "10m")
  • v1beta2 timeoutSeconds field: accepts integer seconds only (e.g., 600)

The minimum allowed value is 100 seconds. The default is 600 seconds.

Example migration:

# v1beta1
spec:
  strategy:
    timeout: 600s
# v1beta2
spec:
  strategy:
    timeoutSeconds: 600
# v1beta1
spec:
  strategy:
    timeout: 10m
# v1beta2
spec:
  strategy:
    timeoutSeconds: 600

Metal3RemediationTemplate

Metal3RemediationTemplate Fields with Added omitzero

Field
spec
status

Conditions Format Change

The conditions type has changed from Cluster API’s clusterv1beta1.Conditions to Kubernetes native metav1.Condition for Metal3Cluster and Metal3Machine:

v1beta1 Condition Typev1beta2 Condition Type
clusterv1beta1.Conditions[]metav1.Condition

This means:

  • v1beta1: Conditions use CAPI-specific format with severity field
  • v1beta2: Conditions use standard Kubernetes format

See the CAPI proposal for more context.

CLI Flag Rename

The CAPM3 CLI flag was renamed:

v1beta1 Flagv1beta2 Flag
enableBMHNameBasedPreallocationenable-bmh-name-based-preallocation

Migration: Update your deployment manifests or command-line arguments to use the new flag name.

Non-Breaking Changes

Unhealthy Annotation Update

The unhealthy annotation has been updated from capi.metal3.io/unhealthy to capm3.metal3.io/unhealthy.

The old annotation is still kept for the migration period but is deprecated. Users are advised to update their deployments to use the new annotation.

Example migration:

# v1beta1 (deprecated, still works)
metadata:
  annotations:
    capi.metal3.io/unhealthy: ""
# v1beta2 (recommended)
metadata:
  annotations:
    capm3.metal3.io/unhealthy: ""

Migration Steps

  1. Backup your current resources: Before migrating, ensure you have backups of all your Metal3 custom resources.

  2. Update Metal3DataTemplate resources:

    • Rename ipAddressesFromIPPool to ipAddressesFromPool
    • Rename prefixesFromIPPool to prefixesFromPool
    • Rename gatewaysFromIPPool to gatewaysFromPool
    • Rename dnsServersFromIPPool to dnsServersFromPool
    • Convert parameters in bond configurations from map to array format
    • Remove any TemplateReference fields
  3. Update Metal3Machine resources:

    • Rename format to diskFormat in spec.image
    • Replace noCloudProvider with cloudProviderEnabled (inverted logic)
  4. Update Metal3Remediation resources:

    • Replace timeout with timeoutSeconds (convert duration to seconds)
  5. Update CAPM3 deployment:

    • Change CLI flag from enableBMHNameBasedPreallocation to enable-bmh-name-based-preallocation
  6. Update annotations (optional but recommended):

    • Update capi.metal3.io/unhealthy to capm3.metal3.io/unhealthy
  7. Update automation/tooling that reads conditions:

    • Update code that checks status.ready to use status.initialization.provisioned (affects Metal3Cluster, Metal3Machine)
    • Update code that reads status.conditions to handle metav1.Condition format (affects Metal3Cluster, Metal3Machine)
    • Update condition type checks from AssociateBMH to AssociateBareMetalHost (affects Metal3Machine)
    • Update condition type checks from KubernetesNodeReady to AssociateMetal3MachineMetaData (affects Metal3Machine)
    • Remove any code that depends on status.phase (affects Metal3Machine, no longer available)
    • Note that status.failureReason and status.failureMessage are now at status.deprecated.v1beta1.* and no longer represent terminal errors (affects Metal3Cluster, Metal3Machine). Use status.conditions for error information instead