Skip to content

Commit c500c89

Browse files
committed
feat: Add k8s version logic for external cloud-provider flag
This flag has been removed from kube-apiserver in k8s v1.33 so need to use version specific logic to add it for pre-1.33 clusters.
1 parent 2fa88f6 commit c500c89

File tree

9 files changed

+290
-38
lines changed

9 files changed

+290
-38
lines changed

charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses/aws-cluster-class.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ spec:
8484
clusterConfiguration:
8585
apiServer:
8686
extraArgs:
87-
cloud-provider: external
8887
profiling: "false"
8988
controllerManager:
9089
extraArgs:

charts/cluster-api-runtime-extensions-nutanix/defaultclusterclasses/nutanix-cluster-class.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ spec:
123123
clusterConfiguration:
124124
apiServer:
125125
extraArgs:
126-
cloud-provider: external
127126
profiling: "false"
128127
tls-cipher-suites: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
129128
controllerManager:

common/pkg/testutils/capitest/patches.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,15 @@ func AssertGeneratePatches[T mutation.GeneratePatches](
6363
}
6464
resp := &runtimehooksv1.GeneratePatchesResponse{}
6565
h.GeneratePatches(context.Background(), req, resp)
66-
expectedStatus := runtimehooksv1.ResponseStatusSuccess
6766
if tt.ExpectedFailure {
68-
expectedStatus = runtimehooksv1.ResponseStatusFailure
67+
g.Expect(resp.Status).
68+
To(gomega.Equal(runtimehooksv1.ResponseStatusFailure), fmt.Sprintf("Message: %s", resp.Message))
69+
return
6970
}
71+
7072
g.Expect(resp.Status).
71-
To(gomega.Equal(expectedStatus), fmt.Sprintf("Message: %s", resp.Message))
73+
To(gomega.Equal(runtimehooksv1.ResponseStatusSuccess), fmt.Sprintf("Message: %s", resp.Message))
7274

73-
if len(tt.ExpectedPatchMatchers) == 0 {
74-
g.Expect(resp.Items).To(gomega.BeEmpty())
75-
return
76-
}
7775
g.Expect(resp.Items).To(containPatches(&tt.RequestItem, tt.ExpectedPatchMatchers...))
7876

7977
if len(tt.UnexpectedPatchMatchers) > 0 {
@@ -111,6 +109,17 @@ func containPatches(
111109
requestItem *runtimehooksv1.GeneratePatchesRequestItem,
112110
jsonMatchers ...JSONPatchMatcher,
113111
) gomega.OmegaMatcher {
112+
if len(jsonMatchers) == 0 {
113+
return gomega.SatisfyAny(
114+
gomega.BeEmpty(),
115+
gomega.ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{
116+
"UID": gomega.Equal(requestItem.UID),
117+
"PatchType": gomega.Equal(runtimehooksv1.JSONPatchType),
118+
"Patch": gomega.Equal([]byte("[]")),
119+
})),
120+
)
121+
}
122+
114123
patchMatchers := make([]interface{}, 0, len(jsonMatchers))
115124
for patchIdx := range jsonMatchers {
116125
unexpectedPatch := jsonMatchers[patchIdx]

common/pkg/testutils/capitest/request/items.go

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
package request
55

66
import (
7+
"maps"
8+
79
corev1 "k8s.io/api/core/v1"
810
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
911
"k8s.io/apimachinery/pkg/runtime"
1012
"k8s.io/apimachinery/pkg/types"
1113
"k8s.io/apimachinery/pkg/util/uuid"
14+
"k8s.io/utils/ptr"
1215
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1316
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
1417
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
@@ -88,7 +91,9 @@ func NewKubeadmConfigTemplateRequest(
8891
}
8992

9093
type KubeadmControlPlaneTemplateRequestItemBuilder struct {
91-
files []bootstrapv1.File
94+
files []bootstrapv1.File
95+
version *string
96+
apiServerExtraArgs map[string]string
9297
}
9398

9499
func (b *KubeadmControlPlaneTemplateRequestItemBuilder) WithFiles(
@@ -98,43 +103,75 @@ func (b *KubeadmControlPlaneTemplateRequestItemBuilder) WithFiles(
98103
return b
99104
}
100105

106+
func (b *KubeadmControlPlaneTemplateRequestItemBuilder) WithKubernetesVersion(
107+
version string,
108+
) *KubeadmControlPlaneTemplateRequestItemBuilder {
109+
b.version = ptr.To(version)
110+
return b
111+
}
112+
113+
func (b *KubeadmControlPlaneTemplateRequestItemBuilder) WithAPIServerExtraArgs(
114+
extraArgs map[string]string,
115+
) *KubeadmControlPlaneTemplateRequestItemBuilder {
116+
b.apiServerExtraArgs = extraArgs
117+
return b
118+
}
119+
101120
func (b *KubeadmControlPlaneTemplateRequestItemBuilder) NewRequest(
102121
uid types.UID,
103122
) runtimehooksv1.GeneratePatchesRequestItem {
104-
return NewRequestItem(
105-
&controlplanev1.KubeadmControlPlaneTemplate{
106-
TypeMeta: metav1.TypeMeta{
107-
APIVersion: controlplanev1.GroupVersion.String(),
108-
Kind: "KubeadmControlPlaneTemplate",
109-
},
110-
ObjectMeta: metav1.ObjectMeta{
111-
Name: kubeadmControlPlaneTemplateRequestObjectName,
112-
Namespace: Namespace,
113-
},
114-
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
115-
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
116-
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
117-
KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
118-
InitConfiguration: &bootstrapv1.InitConfiguration{
119-
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
120-
KubeletExtraArgs: map[string]string{
121-
"cloud-provider": "external",
122-
},
123+
cpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
124+
TypeMeta: metav1.TypeMeta{
125+
APIVersion: controlplanev1.GroupVersion.String(),
126+
Kind: "KubeadmControlPlaneTemplate",
127+
},
128+
ObjectMeta: metav1.ObjectMeta{
129+
Name: kubeadmControlPlaneTemplateRequestObjectName,
130+
Namespace: Namespace,
131+
},
132+
Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
133+
Template: controlplanev1.KubeadmControlPlaneTemplateResource{
134+
Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
135+
KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
136+
InitConfiguration: &bootstrapv1.InitConfiguration{
137+
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
138+
KubeletExtraArgs: map[string]string{
139+
"cloud-provider": "external",
123140
},
124141
},
125-
JoinConfiguration: &bootstrapv1.JoinConfiguration{
126-
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
127-
KubeletExtraArgs: map[string]string{
128-
"cloud-provider": "external",
129-
},
142+
},
143+
JoinConfiguration: &bootstrapv1.JoinConfiguration{
144+
NodeRegistration: bootstrapv1.NodeRegistrationOptions{
145+
KubeletExtraArgs: map[string]string{
146+
"cloud-provider": "external",
130147
},
131148
},
132-
Files: b.files,
133149
},
150+
Files: b.files,
134151
},
135152
},
136153
},
137154
},
155+
}
156+
157+
if b.version != nil {
158+
if cpTemplate.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
159+
cpTemplate.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{}
160+
}
161+
cpTemplate.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.KubernetesVersion = *b.version
162+
}
163+
164+
if b.apiServerExtraArgs != nil {
165+
if cpTemplate.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
166+
cpTemplate.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{}
167+
}
168+
cpTemplate.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer.ExtraArgs = maps.Clone(
169+
b.apiServerExtraArgs,
170+
)
171+
}
172+
173+
return NewRequestItem(
174+
cpTemplate,
138175
&runtimehooksv1.HolderReference{
139176
APIVersion: clusterv1.GroupVersion.String(),
140177
Kind: "Cluster",

hack/examples/bases/aws/clusterclass/kustomization.yaml.tmpl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ patches:
3434
- target:
3535
kind: KubeadmControlPlaneTemplate
3636
patch: |-
37-
- op: "replace"
38-
path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/apiServer/extraArgs/cloud-provider"
39-
value: "external"
4037
- op: "replace"
4138
path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/controllerManager/extraArgs/cloud-provider"
4239
value: "external"
@@ -52,6 +49,13 @@ patches:
5249
- op: "replace"
5350
path: "/spec/template/spec/joinConfiguration/nodeRegistration/kubeletExtraArgs/cloud-provider"
5451
value: "external"
52+
# Delete the API server cloud-provider flag from the template.
53+
# They will be added by the handler for k8s < 1.33.
54+
- target:
55+
kind: KubeadmControlPlaneTemplate
56+
patch: |-
57+
- op: "remove"
58+
path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/apiServer/extraArgs/cloud-provider"
5559

5660
# Delete the cluster-specific resources.
5761
- target:

hack/examples/bases/nutanix/clusterclass/kustomization.yaml.tmpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ patches:
4646
- op: "remove"
4747
path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/apiServer/certSANs"
4848

49+
# TODO: Remove once https://github.com/nutanix-cloud-native/cluster-api-provider-nutanix/pull/519 is
50+
# merged and released.
51+
# Delete the API server cloud-provider flag from the template.
52+
# They will be added by the handler for k8s < 1.33.
53+
- target:
54+
kind: KubeadmControlPlaneTemplate
55+
patch: |-
56+
- op: "remove"
57+
path: "/spec/template/spec/kubeadmConfigSpec/clusterConfiguration/apiServer/extraArgs/cloud-provider"
58+
4959
# Template the kube-vip file.
5060
# The handler will set the variables if needed, or remove it.
5161
- target:
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2023 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package externalcloudprovider
4+
5+
import (
6+
"context"
7+
"errors"
8+
"fmt"
9+
10+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12+
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
13+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
14+
ctrl "sigs.k8s.io/controller-runtime"
15+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
16+
17+
"github.com/blang/semver/v4"
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
19+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
20+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
21+
)
22+
23+
var (
24+
k8sLessThan133Range = semver.MustParseRange("<1.33.0-0")
25+
)
26+
27+
type externalCloudProviderPatchHandler struct{}
28+
29+
func NewPatch() *externalCloudProviderPatchHandler {
30+
return &externalCloudProviderPatchHandler{}
31+
}
32+
33+
func (h *externalCloudProviderPatchHandler) Mutate(
34+
ctx context.Context,
35+
obj *unstructured.Unstructured,
36+
vars map[string]apiextensionsv1.JSON,
37+
holderRef runtimehooksv1.HolderReference,
38+
_ ctrlclient.ObjectKey,
39+
_ mutation.ClusterGetter,
40+
) error {
41+
log := ctrl.LoggerFrom(ctx).WithValues(
42+
"holderRef", holderRef,
43+
)
44+
45+
if err := patches.MutateIfApplicable(
46+
obj, vars, &holderRef, selectors.ControlPlane(), log,
47+
func(obj *controlplanev1.KubeadmControlPlaneTemplate) error {
48+
if obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration == nil ||
49+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.KubernetesVersion == "" {
50+
err := errors.New("missing control plane Kubernetes version")
51+
log.Error(err, "unable to add external cloud-provider flag")
52+
return err
53+
}
54+
55+
cpK8sVersion, err := semver.ParseTolerant(
56+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.KubernetesVersion,
57+
)
58+
if err != nil {
59+
log.WithValues(
60+
"kubernetesVersion",
61+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.KubernetesVersion,
62+
).Error(err, "failed to parse control plane Kubernetes version")
63+
return fmt.Errorf("failed to parse control plane Kubernetes version: %w", err)
64+
}
65+
66+
if k8sLessThan133Range(cpK8sVersion) {
67+
log.WithValues(
68+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
69+
"patchedObjectName", ctrlclient.ObjectKeyFromObject(obj),
70+
).Info(
71+
"adding external cloud-provider flag to control plane kubeadm config template because Kubernetes < 1.33.0",
72+
)
73+
74+
if obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer.ExtraArgs == nil {
75+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer.ExtraArgs = make(map[string]string, 1)
76+
}
77+
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.APIServer.ExtraArgs["cloud-provider"] = "external"
78+
}
79+
80+
return nil
81+
}); err != nil {
82+
return err
83+
}
84+
85+
return nil
86+
}

0 commit comments

Comments
 (0)