Skip to content

Commit 113722d

Browse files
authored
⚠️ Add v1beta2 API for ExtensionConfig (#12197)
* Introduce ExtensionConfig v1beta2 * change import to v1beta2 * implement conversion * fixup webhook * rename deprecated status to v1beta1 * fixup linter * fixup conversion tests * remove again binary * fixup * delete import restrictions for runtime v1alpha1 * fix * generate * extensionconfig: fixes and revert v1beta1 related changes suited only for v1beta2 api package
1 parent da7dbf6 commit 113722d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1665
-61
lines changed

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ linters:
157157
alias: ipamv1
158158
# CAPI exp runtime
159159
- pkg: sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1
160+
alias: runtimev1alpha1
161+
- pkg: sigs.k8s.io/cluster-api/exp/runtime/api/v1beta2
160162
alias: runtimev1
161163
- pkg: sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1
162164
alias: runtimehooksv1

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,12 @@ generate-go-conversions-core-runtime: $(CONVERSION_GEN) ## Generate conversions
494494
--go-header-file=./hack/boilerplate/boilerplate.generatego.txt \
495495
./internal/runtime/test/v1alpha1 \
496496
./internal/runtime/test/v1alpha2
497+
$(MAKE) clean-generated-conversions SRC_DIRS="./$(EXP_DIR)/runtime/api/v1alpha1,./$(EXP_DIR)/runtime/api/v1beta2"
498+
$(CONVERSION_GEN) \
499+
--output-file=zz_generated.conversion.go \
500+
--go-header-file=./hack/boilerplate/boilerplate.generatego.txt \
501+
./$(EXP_DIR)/runtime/api/v1alpha1 \
502+
./$(EXP_DIR)/runtime/api/v1beta2
497503

498504
.PHONY: generate-go-conversions-kubeadm-bootstrap
499505
generate-go-conversions-kubeadm-bootstrap: $(CONVERSION_GEN) ## Generate conversions go code for kubeadm bootstrap

config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml

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

config/crd/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ patches:
3232
- path: patches/webhook_in_clusterresourcesetbindings.yaml
3333
- path: patches/webhook_in_ipaddresses.yaml
3434
- path: patches/webhook_in_ipaddressclaims.yaml
35+
- path: patches/webhook_in_extensionconfigs.yaml
3536
# +kubebuilder:scaffold:crdkustomizewebhookpatch
3637

3738
# the following config is for teaching kustomize how to do kustomization for CRDs.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# The following patch enables conversion webhook for CRD
2+
# CRD conversion requires k8s 1.13 or later.
3+
apiVersion: apiextensions.k8s.io/v1
4+
kind: CustomResourceDefinition
5+
metadata:
6+
name: extensionconfigs.runtime.cluster.x-k8s.io
7+
spec:
8+
conversion:
9+
strategy: Webhook
10+
webhook:
11+
conversionReviewVersions: ["v1", "v1beta1"]
12+
clientConfig:
13+
service:
14+
namespace: system
15+
name: webhook-service
16+
path: /convert

config/default/kustomization.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ replacements:
5757
delimiter: /
5858
select:
5959
kind: CustomResourceDefinition
60-
reject:
61-
- name: extensionconfigs.runtime.cluster.x-k8s.io
6260
- source:
6361
fieldPath: .metadata.name
6462
group: cert-manager.io
@@ -90,8 +88,6 @@ replacements:
9088
index: 1
9189
select:
9290
kind: CustomResourceDefinition
93-
reject:
94-
- name: extensionconfigs.runtime.cluster.x-k8s.io
9591
- source:
9692
fieldPath: .metadata.name
9793
kind: Service

config/webhook/manifests.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,15 @@ webhooks:
165165
service:
166166
name: webhook-service
167167
namespace: system
168-
path: /mutate-runtime-cluster-x-k8s-io-v1alpha1-extensionconfig
168+
path: /mutate-runtime-cluster-x-k8s-io-v1beta2-extensionconfig
169169
failurePolicy: Fail
170170
matchPolicy: Equivalent
171171
name: default.extensionconfig.runtime.addons.cluster.x-k8s.io
172172
rules:
173173
- apiGroups:
174174
- runtime.cluster.x-k8s.io
175175
apiVersions:
176-
- v1alpha1
176+
- v1beta2
177177
operations:
178178
- CREATE
179179
- UPDATE
@@ -415,15 +415,15 @@ webhooks:
415415
service:
416416
name: webhook-service
417417
namespace: system
418-
path: /validate-runtime-cluster-x-k8s-io-v1alpha1-extensionconfig
418+
path: /validate-runtime-cluster-x-k8s-io-v1beta2-extensionconfig
419419
failurePolicy: Fail
420420
matchPolicy: Equivalent
421421
name: validation.extensionconfig.runtime.cluster.x-k8s.io
422422
rules:
423423
- apiGroups:
424424
- runtime.cluster.x-k8s.io
425425
apiVersions:
426-
- v1alpha1
426+
- v1beta2
427427
operations:
428428
- CREATE
429429
- UPDATE
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
apimachineryconversion "k8s.io/apimachinery/pkg/conversion"
22+
"sigs.k8s.io/controller-runtime/pkg/conversion"
23+
24+
clusterv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
25+
runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1beta2"
26+
)
27+
28+
func (src *ExtensionConfig) ConvertTo(dstRaw conversion.Hub) error {
29+
dst := dstRaw.(*runtimev1.ExtensionConfig)
30+
31+
return Convert_v1alpha1_ExtensionConfig_To_v1beta2_ExtensionConfig(src, dst, nil)
32+
}
33+
34+
func (dst *ExtensionConfig) ConvertFrom(srcRaw conversion.Hub) error {
35+
src := srcRaw.(*runtimev1.ExtensionConfig)
36+
37+
return Convert_v1beta2_ExtensionConfig_To_v1alpha1_ExtensionConfig(src, dst, nil)
38+
}
39+
40+
func Convert_v1beta2_ExtensionConfigStatus_To_v1alpha1_ExtensionConfigStatus(in *runtimev1.ExtensionConfigStatus, out *ExtensionConfigStatus, s apimachineryconversion.Scope) error {
41+
if err := autoConvert_v1beta2_ExtensionConfigStatus_To_v1alpha1_ExtensionConfigStatus(in, out, s); err != nil {
42+
return err
43+
}
44+
45+
// Reset conditions from autogenerated conversions
46+
// NOTE: v1beta2 conditions should not be automatically be converted into legacy conditions (v1beta1).
47+
out.Conditions = nil
48+
49+
// Retrieve legacy conditions (v1beta1) from the deprecated field.
50+
if in.Deprecated != nil && in.Deprecated.V1Beta1 != nil {
51+
if in.Deprecated.V1Beta1.Conditions != nil {
52+
clusterv1beta1.Convert_v1beta2_Deprecated_V1Beta1_Conditions_To_v1beta1_Conditions(&in.Deprecated.V1Beta1.Conditions, &out.Conditions)
53+
}
54+
}
55+
56+
// Move new conditions (v1beta2) to the v1beta2 field.
57+
if in.Conditions == nil {
58+
return nil
59+
}
60+
out.V1Beta2 = &ExtensionConfigV1Beta2Status{}
61+
out.V1Beta2.Conditions = in.Conditions
62+
return nil
63+
}
64+
65+
func Convert_v1alpha1_ExtensionConfigStatus_To_v1beta2_ExtensionConfigStatus(in *ExtensionConfigStatus, out *runtimev1.ExtensionConfigStatus, s apimachineryconversion.Scope) error {
66+
if err := autoConvert_v1alpha1_ExtensionConfigStatus_To_v1beta2_ExtensionConfigStatus(in, out, s); err != nil {
67+
return err
68+
}
69+
70+
// Reset conditions from autogenerated conversions
71+
// NOTE: v1beta1 conditions should not be automatically be converted into v1beta2 conditions.
72+
out.Conditions = nil
73+
74+
// Retrieve new conditions (v1beta2) from the v1beta2 field.
75+
if in.V1Beta2 != nil {
76+
out.Conditions = in.V1Beta2.Conditions
77+
}
78+
79+
// Move legacy conditions (v1beta1) to the deprecated field.
80+
if in.Conditions == nil {
81+
return nil
82+
}
83+
84+
if out.Deprecated == nil {
85+
out.Deprecated = &runtimev1.ExtensionConfigDeprecatedStatus{}
86+
}
87+
if out.Deprecated.V1Beta1 == nil {
88+
out.Deprecated.V1Beta1 = &runtimev1.ExtensionConfigV1Beta1DeprecatedStatus{}
89+
}
90+
if in.Conditions != nil {
91+
clusterv1beta1.Convert_v1beta1_Conditions_To_v1beta2_Deprecated_V1Beta1_Conditions(&in.Conditions, &out.Deprecated.V1Beta1.Conditions)
92+
}
93+
return nil
94+
}
95+
96+
func Convert_v1_Condition_To_v1beta1_Condition(in *metav1.Condition, out *clusterv1beta1.Condition, s apimachineryconversion.Scope) error {
97+
return clusterv1beta1.Convert_v1_Condition_To_v1beta1_Condition(in, out, s)
98+
}
99+
100+
func Convert_v1beta1_Condition_To_v1_Condition(in *clusterv1beta1.Condition, out *metav1.Condition, s apimachineryconversion.Scope) error {
101+
return clusterv1beta1.Convert_v1beta1_Condition_To_v1_Condition(in, out, s)
102+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//go:build !race
2+
3+
/*
4+
Copyright 2025 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package v1alpha1
20+
21+
import (
22+
"reflect"
23+
"testing"
24+
25+
fuzz "github.com/google/gofuzz"
26+
"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
27+
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
28+
29+
runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1beta2"
30+
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
31+
)
32+
33+
// Test is disabled when the race detector is enabled (via "//go:build !race" above) because otherwise the fuzz tests would just time out.
34+
35+
func TestFuzzyConversion(t *testing.T) {
36+
t.Run("for ExtensionConfig", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{
37+
Hub: &runtimev1.ExtensionConfig{},
38+
Spoke: &ExtensionConfig{},
39+
FuzzerFuncs: []fuzzer.FuzzerFuncs{ExtensionConfigFuzzFuncs},
40+
}))
41+
}
42+
43+
func ExtensionConfigFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} {
44+
return []interface{}{
45+
hubExtensionConfigStatus,
46+
spokeExtensionConfigStatus,
47+
}
48+
}
49+
50+
func hubExtensionConfigStatus(in *runtimev1.ExtensionConfigStatus, c fuzz.Continue) {
51+
c.FuzzNoCustom(in)
52+
// Drop empty structs with only omit empty fields.
53+
if in.Deprecated != nil {
54+
if in.Deprecated.V1Beta1 == nil || reflect.DeepEqual(in.Deprecated.V1Beta1, &runtimev1.ExtensionConfigV1Beta1DeprecatedStatus{}) {
55+
in.Deprecated = nil
56+
}
57+
}
58+
}
59+
60+
func spokeExtensionConfigStatus(in *ExtensionConfigStatus, c fuzz.Continue) {
61+
c.FuzzNoCustom(in)
62+
// Drop empty structs with only omit empty fields.
63+
if in.V1Beta2 != nil {
64+
if reflect.DeepEqual(in.V1Beta2, &ExtensionConfigV1Beta2Status{}) {
65+
in.V1Beta2 = nil
66+
}
67+
}
68+
}

exp/runtime/api/v1alpha1/doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ limitations under the License.
1515
*/
1616

1717
// Package v1alpha1 contains the v1alpha1 implementation of ExtensionConfig.
18+
// +k8s:conversion-gen=sigs.k8s.io/cluster-api/exp/runtime/api/v1beta2
1819
package v1alpha1

exp/runtime/api/v1alpha1/extensionconfig_types.go

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package v1alpha1
1919
import (
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2121

22-
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta2"
22+
clusterv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
2323
)
2424

2525
// ANCHOR: ExtensionConfigSpec
@@ -125,7 +125,7 @@ type ExtensionConfigStatus struct {
125125

126126
// conditions define the current service state of the ExtensionConfig.
127127
// +optional
128-
Conditions clusterv1.Conditions `json:"conditions,omitempty"`
128+
Conditions clusterv1beta1.Conditions `json:"conditions,omitempty"`
129129

130130
// v1beta2 groups all the fields that will be added or modified in ExtensionConfig's status with the V1Beta2 version.
131131
// +optional
@@ -203,7 +203,7 @@ const (
203203
// +kubebuilder:object:root=true
204204
// +kubebuilder:resource:path=extensionconfigs,shortName=ext,scope=Cluster,categories=cluster-api
205205
// +kubebuilder:subresource:status
206-
// +kubebuilder:storageversion
206+
// +kubebuilder:deprecatedversion
207207
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of ExtensionConfig"
208208

209209
// ExtensionConfig is the Schema for the ExtensionConfig API.
@@ -223,26 +223,26 @@ type ExtensionConfig struct {
223223
Status ExtensionConfigStatus `json:"status,omitempty"`
224224
}
225225

226-
// GetV1Beta1Conditions returns the set of conditions for this object.
227-
func (e *ExtensionConfig) GetV1Beta1Conditions() clusterv1.Conditions {
226+
// GetConditions returns the set of conditions for this object.
227+
func (e *ExtensionConfig) GetConditions() clusterv1beta1.Conditions {
228228
return e.Status.Conditions
229229
}
230230

231-
// SetV1Beta1Conditions sets the conditions on this object.
232-
func (e *ExtensionConfig) SetV1Beta1Conditions(conditions clusterv1.Conditions) {
231+
// SetConditions sets the conditions on this object.
232+
func (e *ExtensionConfig) SetConditions(conditions clusterv1beta1.Conditions) {
233233
e.Status.Conditions = conditions
234234
}
235235

236-
// GetConditions returns the set of conditions for this object.
237-
func (e *ExtensionConfig) GetConditions() []metav1.Condition {
236+
// GetV1Beta2Conditions returns the set of conditions for this object.
237+
func (e *ExtensionConfig) GetV1Beta2Conditions() []metav1.Condition {
238238
if e.Status.V1Beta2 == nil {
239239
return nil
240240
}
241241
return e.Status.V1Beta2.Conditions
242242
}
243243

244-
// SetConditions sets conditions for an API object.
245-
func (e *ExtensionConfig) SetConditions(conditions []metav1.Condition) {
244+
// SetV1Beta2Conditions sets conditions for an API object.
245+
func (e *ExtensionConfig) SetV1Beta2Conditions(conditions []metav1.Condition) {
246246
if e.Status.V1Beta2 == nil {
247247
e.Status.V1Beta2 = &ExtensionConfigV1Beta2Status{}
248248
}
@@ -268,22 +268,22 @@ func init() {
268268

269269
// ExtensionConfig's Discovered conditions and corresponding reasons that will be used in v1Beta2 API version.
270270
const (
271-
// ExtensionConfigDiscoveredCondition is true if the runtime extension has been successfully discovered.
272-
ExtensionConfigDiscoveredCondition = "Discovered"
271+
// ExtensionConfigDiscoveredV1Beta2Condition is true if the runtime extension has been successfully discovered.
272+
ExtensionConfigDiscoveredV1Beta2Condition = "Discovered"
273273

274-
// ExtensionConfigDiscoveredReason surfaces that the runtime extension has been successfully discovered.
275-
ExtensionConfigDiscoveredReason = "Discovered"
274+
// ExtensionConfigDiscoveredV1Beta2Reason surfaces that the runtime extension has been successfully discovered.
275+
ExtensionConfigDiscoveredV1Beta2Reason = "Discovered"
276276

277-
// ExtensionConfigNotDiscoveredReason surfaces that the runtime extension has not been successfully discovered.
278-
ExtensionConfigNotDiscoveredReason = "NotDiscovered"
277+
// ExtensionConfigNotDiscoveredV1Beta2Reason surfaces that the runtime extension has not been successfully discovered.
278+
ExtensionConfigNotDiscoveredV1Beta2Reason = "NotDiscovered"
279279
)
280280

281281
const (
282-
// RuntimeExtensionDiscoveredV1Beta1Condition is a condition set on an ExtensionConfig object once it has been discovered by the Runtime SDK client.
283-
RuntimeExtensionDiscoveredV1Beta1Condition clusterv1.ConditionType = "Discovered"
282+
// RuntimeExtensionDiscoveredCondition is a condition set on an ExtensionConfig object once it has been discovered by the Runtime SDK client.
283+
RuntimeExtensionDiscoveredCondition clusterv1beta1.ConditionType = "Discovered"
284284

285-
// DiscoveryFailedV1Beta1Reason documents failure of a Discovery call.
286-
DiscoveryFailedV1Beta1Reason string = "DiscoveryFailed"
285+
// DiscoveryFailedReason documents failure of a Discovery call.
286+
DiscoveryFailedReason string = "DiscoveryFailed"
287287

288288
// InjectCAFromSecretAnnotation is the annotation that specifies that an ExtensionConfig
289289
// object wants injection of CAs. The value is a reference to a Secret

exp/runtime/api/v1alpha1/groupversion_info.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ var (
3535
// AddToScheme adds the types in this group-version to the given scheme.
3636
AddToScheme = schemeBuilder.AddToScheme
3737

38+
// localSchemeBuilder is used for type conversions.
39+
localSchemeBuilder = schemeBuilder
40+
3841
objectTypes = []runtime.Object{}
3942
)
4043

0 commit comments

Comments
 (0)