Skip to content

Commit 05261de

Browse files
committed
OTA-253: Add cluster update preflight mode API
Implements preflight update mode that allows checking update compatibility without committing to performing the actual cluster update. Changes: - Add optional 'mode' field to Update struct with value "Preflight" - Gate new field behind ClusterUpdatePreflight feature (TechPreviewNoUpgrade) - When mode="Preflight", cluster runs compatibility checks only - When mode omitted, normal update behavior is preserved Related: openshift/enhancements#1930 Signed-off-by: Fabricio Aguiar <fabricio.aguiar@gmail.com> rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED
1 parent 43090db commit 05261de

20 files changed

Lines changed: 1150 additions & 0 deletions
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this
2+
name: "ClusterVersion"
3+
crdName: clusterversions.config.openshift.io
4+
featureGates:
5+
- ClusterUpdatePreflight
6+
tests:
7+
onCreate:
8+
- name: Should be able to create with mode Preflight
9+
initial: |
10+
apiVersion: config.openshift.io/v1
11+
kind: ClusterVersion
12+
spec:
13+
clusterID: foo
14+
desiredUpdate:
15+
version: 4.22.0
16+
mode: Preflight
17+
expected: |
18+
apiVersion: config.openshift.io/v1
19+
kind: ClusterVersion
20+
spec:
21+
clusterID: foo
22+
desiredUpdate:
23+
version: 4.22.0
24+
mode: Preflight
25+
- name: Should be able to omit mode field (default behavior)
26+
initial: |
27+
apiVersion: config.openshift.io/v1
28+
kind: ClusterVersion
29+
spec:
30+
clusterID: foo
31+
desiredUpdate:
32+
version: 4.22.0
33+
expected: |
34+
apiVersion: config.openshift.io/v1
35+
kind: ClusterVersion
36+
spec:
37+
clusterID: foo
38+
desiredUpdate:
39+
version: 4.22.0
40+
- name: Should be able to use Preflight mode with acceptRisks
41+
initial: |
42+
apiVersion: config.openshift.io/v1
43+
kind: ClusterVersion
44+
spec:
45+
clusterID: foo
46+
desiredUpdate:
47+
version: 4.22.0
48+
mode: Preflight
49+
acceptRisks:
50+
- name: RiskA
51+
- name: RiskB
52+
expected: |
53+
apiVersion: config.openshift.io/v1
54+
kind: ClusterVersion
55+
spec:
56+
clusterID: foo
57+
desiredUpdate:
58+
version: 4.22.0
59+
mode: Preflight
60+
acceptRisks:
61+
- name: RiskA
62+
- name: RiskB
63+
- name: Should be able to use Preflight mode with image
64+
initial: |
65+
apiVersion: config.openshift.io/v1
66+
kind: ClusterVersion
67+
spec:
68+
clusterID: foo
69+
desiredUpdate:
70+
image: quay.io/openshift-release-dev/ocp-release@sha256:example
71+
mode: Preflight
72+
expected: |
73+
apiVersion: config.openshift.io/v1
74+
kind: ClusterVersion
75+
spec:
76+
clusterID: foo
77+
desiredUpdate:
78+
image: quay.io/openshift-release-dev/ocp-release@sha256:example
79+
mode: Preflight
80+
- name: Invalid mode value should be rejected
81+
initial: |
82+
apiVersion: config.openshift.io/v1
83+
kind: ClusterVersion
84+
spec:
85+
clusterID: foo
86+
desiredUpdate:
87+
version: 4.22.0
88+
mode: InvalidMode
89+
expectedError: "Unsupported value: \"InvalidMode\""
90+
- name: Empty mode value should be rejected
91+
initial: |
92+
apiVersion: config.openshift.io/v1
93+
kind: ClusterVersion
94+
spec:
95+
clusterID: foo
96+
desiredUpdate:
97+
version: 4.22.0
98+
mode: ""
99+
expectedError: "Unsupported value: \"\""
100+
- name: Should reject when both force and mode are set
101+
initial: |
102+
apiVersion: config.openshift.io/v1
103+
kind: ClusterVersion
104+
spec:
105+
clusterID: foo
106+
desiredUpdate:
107+
version: 4.22.0
108+
mode: Preflight
109+
force: true
110+
expectedError: "force and mode are mutually exclusive"
111+
- name: Should allow Preflight mode when force is explicitly false
112+
initial: |
113+
apiVersion: config.openshift.io/v1
114+
kind: ClusterVersion
115+
spec:
116+
clusterID: foo
117+
desiredUpdate:
118+
version: 4.22.0
119+
mode: Preflight
120+
force: false
121+
expected: |
122+
apiVersion: config.openshift.io/v1
123+
kind: ClusterVersion
124+
spec:
125+
clusterID: foo
126+
desiredUpdate:
127+
version: 4.22.0
128+
mode: Preflight
129+
force: false
130+
onUpdate:
131+
- name: Should be able to transition from normal to Preflight mode
132+
initial: |
133+
apiVersion: config.openshift.io/v1
134+
kind: ClusterVersion
135+
spec:
136+
clusterID: foo
137+
desiredUpdate:
138+
version: 4.22.0
139+
updated: |
140+
apiVersion: config.openshift.io/v1
141+
kind: ClusterVersion
142+
spec:
143+
clusterID: foo
144+
desiredUpdate:
145+
version: 4.22.0
146+
mode: Preflight
147+
expected: |
148+
apiVersion: config.openshift.io/v1
149+
kind: ClusterVersion
150+
spec:
151+
clusterID: foo
152+
desiredUpdate:
153+
version: 4.22.0
154+
mode: Preflight
155+
- name: Should allow clearing Preflight mode back to normal
156+
initial: |
157+
apiVersion: config.openshift.io/v1
158+
kind: ClusterVersion
159+
spec:
160+
clusterID: foo
161+
desiredUpdate:
162+
version: 4.22.0
163+
mode: Preflight
164+
updated: |
165+
apiVersion: config.openshift.io/v1
166+
kind: ClusterVersion
167+
spec:
168+
clusterID: foo
169+
desiredUpdate:
170+
version: 4.22.0
171+
expected: |
172+
apiVersion: config.openshift.io/v1
173+
kind: ClusterVersion
174+
spec:
175+
clusterID: foo
176+
desiredUpdate:
177+
version: 4.22.0
178+
- name: Should allow changing target version while in Preflight mode
179+
initial: |
180+
apiVersion: config.openshift.io/v1
181+
kind: ClusterVersion
182+
spec:
183+
clusterID: foo
184+
desiredUpdate:
185+
version: 4.22.0
186+
mode: Preflight
187+
updated: |
188+
apiVersion: config.openshift.io/v1
189+
kind: ClusterVersion
190+
spec:
191+
clusterID: foo
192+
desiredUpdate:
193+
version: 4.23.0
194+
mode: Preflight
195+
expected: |
196+
apiVersion: config.openshift.io/v1
197+
kind: ClusterVersion
198+
spec:
199+
clusterID: foo
200+
desiredUpdate:
201+
version: 4.23.0
202+
mode: Preflight
203+
- name: Should allow changing from image to version in Preflight mode
204+
initial: |
205+
apiVersion: config.openshift.io/v1
206+
kind: ClusterVersion
207+
spec:
208+
clusterID: foo
209+
desiredUpdate:
210+
image: quay.io/openshift-release-dev/ocp-release@sha256:example
211+
mode: Preflight
212+
updated: |
213+
apiVersion: config.openshift.io/v1
214+
kind: ClusterVersion
215+
spec:
216+
clusterID: foo
217+
desiredUpdate:
218+
version: 4.23.0
219+
mode: Preflight
220+
expected: |
221+
apiVersion: config.openshift.io/v1
222+
kind: ClusterVersion
223+
spec:
224+
clusterID: foo
225+
desiredUpdate:
226+
version: 4.23.0
227+
mode: Preflight

config/v1/types_cluster_version.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,16 @@ type UpdateHistory struct {
283283
// ClusterID is string RFC4122 uuid.
284284
type ClusterID string
285285

286+
// UpdateMode defines how an update should be processed.
287+
// +enum
288+
// +kubebuilder:validation:Enum=Preflight
289+
type UpdateMode string
290+
291+
const (
292+
// UpdateModePreflight allows an update to be checked for compatibility without committing to updating the cluster.
293+
UpdateModePreflight UpdateMode = "Preflight"
294+
)
295+
286296
// ClusterVersionArchitecture enumerates valid cluster architectures.
287297
// +kubebuilder:validation:Enum="Multi";""
288298
type ClusterVersionArchitecture string
@@ -704,6 +714,7 @@ type URL string
704714
// Update represents an administrator update request.
705715
// +kubebuilder:validation:XValidation:rule="has(self.architecture) && has(self.image) ? (self.architecture == \"\" || self.image == \"\") : true",message="cannot set both Architecture and Image"
706716
// +kubebuilder:validation:XValidation:rule="has(self.architecture) && self.architecture != \"\" ? self.version != \"\" : true",message="Version must be set if Architecture is set"
717+
// +openshift:validation:FeatureGateAwareXValidation:featureGate=ClusterUpdatePreflight,rule="!has(self.mode) || !has(self.force) || !self.force",message="force and mode are mutually exclusive"
707718
// +k8s:deepcopy-gen=true
708719
type Update struct {
709720
// architecture is an optional field that indicates the desired
@@ -760,6 +771,23 @@ type Update struct {
760771
// +listMapKey=name
761772
// +optional
762773
AcceptRisks []AcceptRisk `json:"acceptRisks,omitempty"`
774+
775+
// mode determines how an update should be processed.
776+
// The only valid value is "Preflight".
777+
// When omitted, the cluster performs a normal update by applying the specified version or image to the cluster.
778+
// This is the standard update behavior.
779+
// When set to "Preflight", the cluster runs compatibility checks against the target release without
780+
// performing an actual update. Compatibility results, including any detected risks, are reported
781+
// in status.conditionalUpdates and status.conditionalUpdateRisks alongside risks from the update
782+
// recommendation service.
783+
// This allows administrators to assess update readiness and address issues before committing to the update.
784+
// Preflight mode is particularly useful for skip-level updates where upgrade compatibility needs to be
785+
// verified across multiple minor versions.
786+
// When mode is set to "Preflight", the same rules for version, image, and architecture apply as for normal updates.
787+
// The mode and force fields are mutually exclusive - mode cannot be set when force is true.
788+
// +openshift:enable:FeatureGate=ClusterUpdatePreflight
789+
// +optional
790+
Mode UpdateMode `json:"mode,omitempty"`
763791
}
764792

765793
// AcceptRisk represents a risk that is considered acceptable.

config/v1/zz_generated.crd-manifests/0000_00_cluster-version-operator_01_clusterversions-CustomNoUpgrade.crd.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,24 @@ spec:
218218
When image is set, architecture cannot be specified.
219219
If both version and image are set, the version extracted from the referenced image must match the specified version.
220220
type: string
221+
mode:
222+
description: |-
223+
mode determines how an update should be processed.
224+
The only valid value is "Preflight".
225+
When omitted, the cluster performs a normal update by applying the specified version or image to the cluster.
226+
This is the standard update behavior.
227+
When set to "Preflight", the cluster runs compatibility checks against the target release without
228+
performing an actual update. Compatibility results, including any detected risks, are reported
229+
in status.conditionalUpdates and status.conditionalUpdateRisks alongside risks from the update
230+
recommendation service.
231+
This allows administrators to assess update readiness and address issues before committing to the update.
232+
Preflight mode is particularly useful for skip-level updates where upgrade compatibility needs to be
233+
verified across multiple minor versions.
234+
When mode is set to "Preflight", the same rules for version, image, and architecture apply as for normal updates.
235+
The mode and force fields are mutually exclusive - mode cannot be set when force is true.
236+
enum:
237+
- Preflight
238+
type: string
221239
version:
222240
description: |-
223241
version is a semantic version identifying the update version.
@@ -226,6 +244,8 @@ spec:
226244
type: string
227245
type: object
228246
x-kubernetes-validations:
247+
- message: force and mode are mutually exclusive
248+
rule: '!has(self.mode) || !has(self.force) || !self.force'
229249
- message: cannot set both Architecture and Image
230250
rule: 'has(self.architecture) && has(self.image) ? (self.architecture
231251
== "" || self.image == "") : true'

config/v1/zz_generated.crd-manifests/0000_00_cluster-version-operator_01_clusterversions-DevPreviewNoUpgrade.crd.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,24 @@ spec:
218218
When image is set, architecture cannot be specified.
219219
If both version and image are set, the version extracted from the referenced image must match the specified version.
220220
type: string
221+
mode:
222+
description: |-
223+
mode determines how an update should be processed.
224+
The only valid value is "Preflight".
225+
When omitted, the cluster performs a normal update by applying the specified version or image to the cluster.
226+
This is the standard update behavior.
227+
When set to "Preflight", the cluster runs compatibility checks against the target release without
228+
performing an actual update. Compatibility results, including any detected risks, are reported
229+
in status.conditionalUpdates and status.conditionalUpdateRisks alongside risks from the update
230+
recommendation service.
231+
This allows administrators to assess update readiness and address issues before committing to the update.
232+
Preflight mode is particularly useful for skip-level updates where upgrade compatibility needs to be
233+
verified across multiple minor versions.
234+
When mode is set to "Preflight", the same rules for version, image, and architecture apply as for normal updates.
235+
The mode and force fields are mutually exclusive - mode cannot be set when force is true.
236+
enum:
237+
- Preflight
238+
type: string
221239
version:
222240
description: |-
223241
version is a semantic version identifying the update version.
@@ -226,6 +244,8 @@ spec:
226244
type: string
227245
type: object
228246
x-kubernetes-validations:
247+
- message: force and mode are mutually exclusive
248+
rule: '!has(self.mode) || !has(self.force) || !self.force'
229249
- message: cannot set both Architecture and Image
230250
rule: 'has(self.architecture) && has(self.image) ? (self.architecture
231251
== "" || self.image == "") : true'

config/v1/zz_generated.crd-manifests/0000_00_cluster-version-operator_01_clusterversions-TechPreviewNoUpgrade.crd.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,24 @@ spec:
218218
When image is set, architecture cannot be specified.
219219
If both version and image are set, the version extracted from the referenced image must match the specified version.
220220
type: string
221+
mode:
222+
description: |-
223+
mode determines how an update should be processed.
224+
The only valid value is "Preflight".
225+
When omitted, the cluster performs a normal update by applying the specified version or image to the cluster.
226+
This is the standard update behavior.
227+
When set to "Preflight", the cluster runs compatibility checks against the target release without
228+
performing an actual update. Compatibility results, including any detected risks, are reported
229+
in status.conditionalUpdates and status.conditionalUpdateRisks alongside risks from the update
230+
recommendation service.
231+
This allows administrators to assess update readiness and address issues before committing to the update.
232+
Preflight mode is particularly useful for skip-level updates where upgrade compatibility needs to be
233+
verified across multiple minor versions.
234+
When mode is set to "Preflight", the same rules for version, image, and architecture apply as for normal updates.
235+
The mode and force fields are mutually exclusive - mode cannot be set when force is true.
236+
enum:
237+
- Preflight
238+
type: string
221239
version:
222240
description: |-
223241
version is a semantic version identifying the update version.
@@ -226,6 +244,8 @@ spec:
226244
type: string
227245
type: object
228246
x-kubernetes-validations:
247+
- message: force and mode are mutually exclusive
248+
rule: '!has(self.mode) || !has(self.force) || !self.force'
229249
- message: cannot set both Architecture and Image
230250
rule: 'has(self.architecture) && has(self.image) ? (self.architecture
231251
== "" || self.image == "") : true'

config/v1/zz_generated.featuregated-crd-manifests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ clusterversions.config.openshift.io:
144144
Category: ""
145145
FeatureGates:
146146
- ClusterUpdateAcceptRisks
147+
- ClusterUpdatePreflight
147148
- ImageStreamImportMode
148149
- SignatureStores
149150
FilenameOperatorName: cluster-version-operator

0 commit comments

Comments
 (0)