Skip to content

Commit 1be387b

Browse files
committed
ClusterExtensionRevision API Updates
Fixing some missing flags and godoc comments brought up via API review. Signed-off-by: Daniel Franz <dfranz@redhat.com>
1 parent 910fa59 commit 1be387b

13 files changed

Lines changed: 261 additions & 122 deletions

api/v1/clusterextensionrevision_types.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,18 @@ const (
4242
type ClusterExtensionRevisionSpec struct {
4343
// lifecycleState specifies the lifecycle state of the ClusterExtensionRevision.
4444
//
45-
// When set to "Active" (the default), the revision is actively managed and reconciled.
45+
// When set to "Active", the revision is actively managed and reconciled.
4646
// When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted.
4747
// The revision is removed from the owner list of all objects previously under management.
4848
// All objects that did not transition to a succeeding revision are deleted.
4949
//
5050
// Once a revision is set to "Archived", it cannot be un-archived.
5151
//
52-
// +kubebuilder:default="Active"
52+
// It is possible for more than one revision to be "Active" simultaneously. This will occur when
53+
// moving from one revision to another. The old revision will not be set to "Archived" until the
54+
// new revision has been completely rolled out.
55+
//
56+
// +required
5357
// +kubebuilder:validation:Enum=Active;Archived
5458
// +kubebuilder:validation:XValidation:rule="oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == self", message="cannot un-archive"
5559
LifecycleState ClusterExtensionRevisionLifecycleState `json:"lifecycleState,omitempty"`
@@ -66,7 +70,8 @@ type ClusterExtensionRevisionSpec struct {
6670
// +kubebuilder:validation:XValidation:rule="self == oldSelf", message="revision is immutable"
6771
Revision int64 `json:"revision"`
6872

69-
// phases is an optional, immutable list of phases that group objects to be applied together.
73+
// inlinePhases is an optional, immutable list of phases that group objects to be applied together. Phases declared in
74+
// this manner are included in the ClusterExtensionRevision as-is, as opposed to being sharded across multiple resources.
7075
//
7176
// Objects are organized into phases based on their Group-Kind. Common phases include:
7277
// - namespaces: Namespace objects
@@ -80,13 +85,16 @@ type ClusterExtensionRevisionSpec struct {
8085
// All objects in a phase are applied in no particular order.
8186
// The revision progresses to the next phase only after all objects in the current phase pass their readiness probes.
8287
//
83-
// Once set, even if empty, the phases field is immutable.
88+
// Once set, even if empty, the inlinePhases field is immutable.
89+
//
90+
// Each phase in the list must have a unique name. The minimum number of inlinePhases is 1, and the maximum is 20.
8491
//
8592
// +kubebuilder:validation:XValidation:rule="self == oldSelf || oldSelf.size() == 0", message="phases is immutable"
93+
// +kubebuilder:validation:MaxItems=20
8694
// +listType=map
8795
// +listMapKey=name
8896
// +optional
89-
Phases []ClusterExtensionRevisionPhase `json:"phases,omitempty"`
97+
InlinePhases []ClusterExtensionRevisionPhase `json:"inlinePhases,omitempty"`
9098

9199
// progressDeadlineMinutes is an optional field that defines the maximum period
92100
// of time in minutes after which an installation should be considered failed and
@@ -125,13 +133,17 @@ type ClusterExtensionRevisionPhase struct {
125133
//
126134
// [RFC 1123]: https://tools.ietf.org/html/rfc1123
127135
//
136+
// +required
137+
// +kubebuilder:validation:MinLength=1
128138
// +kubebuilder:validation:MaxLength=63
129-
// +kubebuilder:validation:Pattern=`^[a-z]([-a-z0-9]*[a-z0-9])?$`
139+
// +kubebuilder:validation:XValidation:rule=`!format.dns1123Label().validate(self).hasValue()`,message="the value must consist of only lowercase alphanumeric characters and hyphens, and must start with an alphabetic character and end with an alphanumeric character."
130140
Name string `json:"name"`
131141

132142
// objects is a required list of all Kubernetes objects that belong to this phase.
133143
//
134-
// All objects in this list are applied to the cluster in no particular order.
144+
// All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50.
145+
// +required
146+
// +kubebuilder:validation:MaxItems=50
135147
Objects []ClusterExtensionRevisionObject `json:"objects"`
136148
}
137149

@@ -149,6 +161,8 @@ type ClusterExtensionRevisionObject struct {
149161
// collisionProtection controls whether the operator can adopt and modify objects
150162
// that already exist on the cluster.
151163
//
164+
// Allowed values are: "Prevent", "IfNoController", and "None".
165+
//
152166
// When set to "Prevent" (the default), the operator only manages objects it created itself.
153167
// This prevents ownership collisions.
154168
//
@@ -161,9 +175,8 @@ type ClusterExtensionRevisionObject struct {
161175
// Use this setting with extreme caution as it may cause multiple controllers to fight over
162176
// the same resource, resulting in increased load on the API server and etcd.
163177
//
164-
// +kubebuilder:default="Prevent"
178+
// +required
165179
// +kubebuilder:validation:Enum=Prevent;IfNoController;None
166-
// +optional
167180
CollisionProtection CollisionProtection `json:"collisionProtection,omitempty"`
168181
}
169182

api/v1/clusterextensionrevision_types_test.go

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,21 @@ func TestClusterExtensionRevisionImmutability(t *testing.T) {
2121
}{
2222
"revision is immutable": {
2323
spec: ClusterExtensionRevisionSpec{
24-
Revision: 1,
24+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
25+
Revision: 1,
2526
},
2627
updateFunc: func(cer *ClusterExtensionRevision) {
2728
cer.Spec.Revision = 2
2829
},
2930
},
3031
"phases may be initially empty": {
3132
spec: ClusterExtensionRevisionSpec{
32-
Revision: 1,
33-
Phases: []ClusterExtensionRevisionPhase{},
33+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
34+
Revision: 1,
35+
InlinePhases: []ClusterExtensionRevisionPhase{},
3436
},
3537
updateFunc: func(cer *ClusterExtensionRevision) {
36-
cer.Spec.Phases = []ClusterExtensionRevisionPhase{
38+
cer.Spec.InlinePhases = []ClusterExtensionRevisionPhase{
3739
{
3840
Name: "foo",
3941
Objects: []ClusterExtensionRevisionObject{},
@@ -44,10 +46,11 @@ func TestClusterExtensionRevisionImmutability(t *testing.T) {
4446
},
4547
"phases may be initially unset": {
4648
spec: ClusterExtensionRevisionSpec{
47-
Revision: 1,
49+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
50+
Revision: 1,
4851
},
4952
updateFunc: func(cer *ClusterExtensionRevision) {
50-
cer.Spec.Phases = []ClusterExtensionRevisionPhase{
53+
cer.Spec.InlinePhases = []ClusterExtensionRevisionPhase{
5154
{
5255
Name: "foo",
5356
Objects: []ClusterExtensionRevisionObject{},
@@ -58,16 +61,17 @@ func TestClusterExtensionRevisionImmutability(t *testing.T) {
5861
},
5962
"phases are immutable if not empty": {
6063
spec: ClusterExtensionRevisionSpec{
61-
Revision: 1,
62-
Phases: []ClusterExtensionRevisionPhase{
64+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
65+
Revision: 1,
66+
InlinePhases: []ClusterExtensionRevisionPhase{
6367
{
6468
Name: "foo",
6569
Objects: []ClusterExtensionRevisionObject{},
6670
},
6771
},
6872
},
6973
updateFunc: func(cer *ClusterExtensionRevision) {
70-
cer.Spec.Phases = []ClusterExtensionRevisionPhase{
74+
cer.Spec.InlinePhases = []ClusterExtensionRevisionPhase{
7175
{
7276
Name: "foo2",
7377
Objects: []ClusterExtensionRevisionObject{},
@@ -107,20 +111,87 @@ func TestClusterExtensionRevisionValidity(t *testing.T) {
107111
}{
108112
"revision cannot be negative": {
109113
spec: ClusterExtensionRevisionSpec{
110-
Revision: -1,
114+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
115+
Revision: -1,
111116
},
112117
valid: false,
113118
},
114119
"revision cannot be zero": {
115-
spec: ClusterExtensionRevisionSpec{},
120+
spec: ClusterExtensionRevisionSpec{
121+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
122+
},
116123
valid: false,
117124
},
118125
"revision must be positive": {
119126
spec: ClusterExtensionRevisionSpec{
120-
Revision: 1,
127+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
128+
Revision: 1,
121129
},
122130
valid: true,
123131
},
132+
"lifecycleState must be set": {
133+
spec: ClusterExtensionRevisionSpec{
134+
Revision: 1,
135+
},
136+
valid: false,
137+
},
138+
"inlinePhases must have no more than 20 phases": {
139+
spec: ClusterExtensionRevisionSpec{
140+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
141+
Revision: 1,
142+
InlinePhases: make([]ClusterExtensionRevisionPhase, 21),
143+
},
144+
valid: false,
145+
},
146+
"inlinePhases entries must have no more than 50 objects": {
147+
spec: ClusterExtensionRevisionSpec{
148+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
149+
Revision: 1,
150+
InlinePhases: []ClusterExtensionRevisionPhase{
151+
{
152+
Name: "too-many-objects",
153+
Objects: make([]ClusterExtensionRevisionObject, 51),
154+
},
155+
},
156+
},
157+
valid: false,
158+
},
159+
"inlinePhases entry names cannot be empty": {
160+
spec: ClusterExtensionRevisionSpec{
161+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
162+
Revision: 1,
163+
InlinePhases: []ClusterExtensionRevisionPhase{
164+
{
165+
Name: "",
166+
},
167+
},
168+
},
169+
valid: false,
170+
},
171+
"inlinePhases entry names cannot start with symbols": {
172+
spec: ClusterExtensionRevisionSpec{
173+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
174+
Revision: 1,
175+
InlinePhases: []ClusterExtensionRevisionPhase{
176+
{
177+
Name: "-invalid",
178+
},
179+
},
180+
},
181+
valid: false,
182+
},
183+
"inlinePhases entry names cannot start with numeric characters": {
184+
spec: ClusterExtensionRevisionSpec{
185+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
186+
Revision: 1,
187+
InlinePhases: []ClusterExtensionRevisionPhase{
188+
{
189+
Name: "1-invalid",
190+
},
191+
},
192+
},
193+
valid: false,
194+
},
124195
} {
125196
t.Run(name, func(t *testing.T) {
126197
cer := &ClusterExtensionRevision{

api/v1/validation_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func TestValidate(t *testing.T) {
9191
"ClusterExtensionRevision: invalid progress deadline < 10": {
9292
args: args{
9393
object: ClusterExtensionRevisionSpec{
94+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
9495
ProgressDeadlineMinutes: 9,
9596
},
9697
},
@@ -99,6 +100,7 @@ func TestValidate(t *testing.T) {
99100
"ClusterExtensionRevision: valid progress deadline = 10": {
100101
args: args{
101102
object: ClusterExtensionRevisionSpec{
103+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
102104
ProgressDeadlineMinutes: 10,
103105
},
104106
},
@@ -107,6 +109,7 @@ func TestValidate(t *testing.T) {
107109
"ClusterExtensionRevision: valid progress deadline = 360": {
108110
args: args{
109111
object: ClusterExtensionRevisionSpec{
112+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
110113
ProgressDeadlineMinutes: 360,
111114
},
112115
},
@@ -115,6 +118,7 @@ func TestValidate(t *testing.T) {
115118
"ClusterExtensionRevision: valid progress deadline = 720": {
116119
args: args{
117120
object: ClusterExtensionRevisionSpec{
121+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
118122
ProgressDeadlineMinutes: 720,
119123
},
120124
},
@@ -123,14 +127,17 @@ func TestValidate(t *testing.T) {
123127
"ClusterExtensionRevision: invalid progress deadline > 720": {
124128
args: args{
125129
object: ClusterExtensionRevisionSpec{
130+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
126131
ProgressDeadlineMinutes: 721,
127132
},
128133
},
129134
want: want{valid: false},
130135
},
131136
"ClusterExtensionRevision: no progress deadline set": {
132137
args: args{
133-
object: ClusterExtensionRevisionSpec{},
138+
object: ClusterExtensionRevisionSpec{
139+
LifecycleState: ClusterExtensionRevisionLifecycleStateActive,
140+
},
134141
},
135142
want: want{valid: true},
136143
},

api/v1/zz_generated.deepcopy.go

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

0 commit comments

Comments
 (0)