Skip to content
15 changes: 15 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ linters:
# to enforce specific condition types are always present.
path: etcd/v1/types_pacemakercluster.go
text: "conditions: Conditions field in (PacemakerClusterStatus|PacemakerClusterNodeStatus|PacemakerClusterFencingAgentStatus|PacemakerClusterResourceStatus) is missing the following markers: optional"
- linters:
- kubeapilinter
# Empty lists are meaningful: no available transitions, or no completed evaluation condition yet.
path: config/v1/types_infrastructure_transitions.go
text: 'minlength: field TopologyTransitionStatus\.(AvailableTransitions|Conditions) must have a minimum items'
- linters:
- kubeapilinter
# Keep the pointer so nil means no topology transition has started; omitempty omits nil.
path: config/v1/types_infrastructure_transitions.go
text: 'optionalfields: field TopologyTransitionStatus\.CurrentTransition'
- linters:
- kubeapilinter
# Keep an evaluated empty list serialized as [] rather than omitting this required field.
path: config/v1/types_infrastructure_transitions.go
text: 'requiredfields: field TopologyTransitionStatus\.AvailableTransitions should have the omitempty tag'
- linters:
- kubeapilinter
path: features|payload-command/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,29 @@ tests:
type: UserManaged
type: BareMetal
expectedStatusError: 'status.platformStatus.baremetal.loadBalancer.type: Invalid value: "string": type is immutable once set'
- name: Should prune topologyTransitionStatus when MutableTopology is disabled
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
topologyTransitionStatus:
availableTransitions: []
expected: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: HighlyAvailable
cpuPartitioning: None
- name: Should not allow changing the immutable OpenStack load balancer type field
initial: |
apiVersion: config.openshift.io/v1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,227 @@ tests:
spec:
controlPlaneTopology: InvalidValue
expectedError: 'spec.controlPlaneTopology: Unsupported value: "InvalidValue": supported values: "HighlyAvailable", "SingleReplica"'
- name: Should allow updating Infrastructure status without topologyTransitionStatus
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: HighlyAvailable
expected: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: HighlyAvailable
cpuPartitioning: None
- name: Should allow an empty availableTransitions list after successful evaluation
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
topologyTransitionStatus:
availableTransitions: []
conditions:
- type: TopologyTransitionsEvaluated
status: "True"
lastTransitionTime: "2025-01-01T00:00:00Z"
reason: EvaluationComplete
message: No transitions are available
expected: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: HighlyAvailable
cpuPartitioning: None
topologyTransitionStatus:
availableTransitions: []
conditions:
- type: TopologyTransitionsEvaluated
status: "True"
lastTransitionTime: "2025-01-01T00:00:00Z"
reason: EvaluationComplete
message: No transitions are available
- name: Should allow reporting current transition progress
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
topologyTransitionStatus:
availableTransitions:
- source:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
target:
controlPlaneTopology: HighlyAvailable
infrastructureTopology: HighlyAvailable
reason: PreflightCheckFailed
currentTransition:
status: Partial
reason: TransitionInProgress
message: Applying topology changes
startedTime: "2025-01-01T00:00:00Z"
expected: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
cpuPartitioning: None
topologyTransitionStatus:
availableTransitions:
- source:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
target:
controlPlaneTopology: HighlyAvailable
infrastructureTopology: HighlyAvailable
reason: PreflightCheckFailed
currentTransition:
status: Partial
reason: TransitionInProgress
message: Applying topology changes
startedTime: "2025-01-01T00:00:00Z"
- name: Should reject a topology transition reason that is not CamelCase
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
topologyTransitionStatus:
availableTransitions:
- source:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
target:
controlPlaneTopology: HighlyAvailable
infrastructureTopology: HighlyAvailable
reason: preflightCheckFailed
expectedStatusError: "reason must be CamelCase, matching ^[A-Z][A-Za-z0-9]*$"
- name: Should reject an available transition whose source does not match status topology
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
topologyTransitionStatus:
availableTransitions:
- source:
controlPlaneTopology: HighlyAvailable
infrastructureTopology: HighlyAvailable
target:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
expectedStatusError: "transition sources must match the current status topology"
- name: Should enforce the supported maximum available transition count
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
topologyTransitionStatus:
availableTransitions:
- source:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
target:
controlPlaneTopology: HighlyAvailable
infrastructureTopology: HighlyAvailable
- source:
controlPlaneTopology: SingleReplica
infrastructureTopology: SingleReplica
target:
controlPlaneTopology: HighlyAvailable
infrastructureTopology: HighlyAvailable
expectedStatusError: "Too many: 2: must have at most 1 items"
- name: Should require availableTransitions when topologyTransitionStatus is set
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
topologyTransitionStatus:
conditions: []
expectedStatusError: "Required value"
- name: Should require status on currentTransition
initial: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
updated: |
apiVersion: config.openshift.io/v1
kind: Infrastructure
spec: {}
status:
controlPlaneTopology: SingleReplica
topologyTransitionStatus:
availableTransitions: []
currentTransition:
reason: TransitionInProgress
message: Applying topology changes
expectedStatusError: "Required value"
7 changes: 7 additions & 0 deletions config/v1/types_infrastructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
// +kubebuilder:subresource:status
// +kubebuilder:metadata:annotations=release.openshift.io/bootstrap-required=true
// +openshift:validation:FeatureGateAwareXValidation:featureGate=MutableTopology,rule="!has(self.spec.controlPlaneTopology) || (has(oldSelf.spec.controlPlaneTopology) && self.spec.controlPlaneTopology == oldSelf.spec.controlPlaneTopology) || (has(self.status.controlPlaneTopology) && self.spec.controlPlaneTopology == self.status.controlPlaneTopology) || (has(self.status.controlPlaneTopology) && self.status.controlPlaneTopology == 'SingleReplica' && self.spec.controlPlaneTopology == 'HighlyAvailable')",message="spec.controlPlaneTopology must match status.controlPlaneTopology or be set to HighlyAvailable when status.controlPlaneTopology is SingleReplica"
// +openshift:validation:FeatureGateAwareXValidation:featureGate=MutableTopology,rule="!has(self.status) || !has(self.status.topologyTransitionStatus) || self.status.topologyTransitionStatus.availableTransitions.all(t, has(self.status.controlPlaneTopology) && has(self.status.infrastructureTopology) && t.source.controlPlaneTopology == self.status.controlPlaneTopology && t.source.infrastructureTopology == self.status.infrastructureTopology)",message="transition sources must match the current status topology"
type Infrastructure struct {
metav1.TypeMeta `json:",inline"`

Expand Down Expand Up @@ -138,6 +139,12 @@ type InfrastructureStatus struct {
// +optional
InfrastructureTopology TopologyMode `json:"infrastructureTopology,omitempty"`

// topologyTransitionStatus reports available topology transitions and current progress, if any.
// It is omitted until the topology controller evaluates transitions.
// +openshift:enable:FeatureGate=MutableTopology
// +optional
TopologyTransitionStatus *TopologyTransitionStatus `json:"topologyTransitionStatus,omitempty"`

// cpuPartitioning expresses if CPU partitioning is a currently enabled feature in the cluster.
// CPU Partitioning means that this cluster can support partitioning workloads to specific CPU Sets.
// Valid values are "None" and "AllNodes". When omitted, the default value is "None".
Expand Down
Loading