Skip to content

Commit 88c3519

Browse files
committed
feat: Allow configuration of kube-proxy mode on cluster creation
This commit allows users to configure kube-proxy mode when creating clusters. This is enforced via CEL to prevent users from moving between kube-proxy and non-kube-proxy deployments. Currently only `Disabled` and `iptables` modes are supported due to limitations in CAPI kube-proxy configuration. Follow up work will allow Cilium configuration to enable their kube-proxy replacement (already possible via custom Helm values) and migration from kube-proxy to kube-proxy replacement for existing clusters.
1 parent a6cccf3 commit 88c3519

File tree

11 files changed

+620
-0
lines changed

11 files changed

+620
-0
lines changed

api/v1alpha1/clusterconfig_types.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ type GenericClusterConfigSpec struct {
212212

213213
// +kubebuilder:validation:Optional
214214
DNS *DNS `json:"dns,omitempty"`
215+
216+
// KubeProxy defines the configuration for kube-proxy.
217+
// +kubebuilder:validation:Optional
218+
KubeProxy *KubeProxy `json:"kubeProxy,omitempty"`
215219
}
216220

217221
type Image struct {
@@ -328,6 +332,27 @@ type CoreDNS struct {
328332
Image *Image `json:"image,omitempty"`
329333
}
330334

335+
type KubeProxyMode string
336+
337+
const (
338+
// KubeProxyModeDisabled indicates that kube-proxy should not be installed.
339+
KubeProxyModeDisabled KubeProxyMode = "Disabled"
340+
// KubeProxyModeIPTables indicates that kube-proxy should be installed in iptables
341+
// mode.
342+
KubeProxyModeIPTables KubeProxyMode = "iptables"
343+
)
344+
345+
type KubeProxy struct {
346+
// Mode specifies the mode for kube-proxy.
347+
// Disabled means that kube-proxy is not installed.
348+
// iptables means that kube-proxy is installed in iptables mode.
349+
// +kubebuilder:validation:Optional
350+
// +kubebuilder:validation:Enum=Disabled;iptables
351+
// +kubebuilder:default=iptables
352+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value cannot be changed after cluster creation"
353+
Mode KubeProxyMode `json:"mode,omitempty"`
354+
}
355+
331356
//nolint:gochecknoinits // Idiomatic to use init functions to register APIs with scheme.
332357
func init() {
333358
objectTypes = append(objectTypes,

api/v1alpha1/crds/caren.nutanix.com_awsclusterconfigs.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,23 @@ spec:
560560
- url
561561
type: object
562562
type: array
563+
kubeProxy:
564+
description: KubeProxy defines the configuration for kube-proxy.
565+
properties:
566+
mode:
567+
default: iptables
568+
description: |-
569+
Mode specifies the mode for kube-proxy.
570+
Disabled means that kube-proxy is not installed.
571+
iptables means that kube-proxy is installed in iptables mode.
572+
enum:
573+
- Disabled
574+
- iptables
575+
type: string
576+
x-kubernetes-validations:
577+
- message: Value cannot be changed after cluster creation
578+
rule: self == oldSelf
579+
type: object
563580
kubernetesImageRepository:
564581
description: Sets the Kubernetes image repository used for the KubeadmControlPlane.
565582
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$

api/v1alpha1/crds/caren.nutanix.com_dockerclusterconfigs.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,23 @@ spec:
497497
- url
498498
type: object
499499
type: array
500+
kubeProxy:
501+
description: KubeProxy defines the configuration for kube-proxy.
502+
properties:
503+
mode:
504+
default: iptables
505+
description: |-
506+
Mode specifies the mode for kube-proxy.
507+
Disabled means that kube-proxy is not installed.
508+
iptables means that kube-proxy is installed in iptables mode.
509+
enum:
510+
- Disabled
511+
- iptables
512+
type: string
513+
x-kubernetes-validations:
514+
- message: Value cannot be changed after cluster creation
515+
rule: self == oldSelf
516+
type: object
500517
kubernetesImageRepository:
501518
description: Sets the Kubernetes image repository used for the KubeadmControlPlane.
502519
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$

api/v1alpha1/crds/caren.nutanix.com_genericclusterconfigs.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,23 @@ spec:
175175
- url
176176
type: object
177177
type: array
178+
kubeProxy:
179+
description: KubeProxy defines the configuration for kube-proxy.
180+
properties:
181+
mode:
182+
default: iptables
183+
description: |-
184+
Mode specifies the mode for kube-proxy.
185+
Disabled means that kube-proxy is not installed.
186+
iptables means that kube-proxy is installed in iptables mode.
187+
enum:
188+
- Disabled
189+
- iptables
190+
type: string
191+
x-kubernetes-validations:
192+
- message: Value cannot be changed after cluster creation
193+
rule: self == oldSelf
194+
type: object
178195
kubernetesImageRepository:
179196
description: Sets the Kubernetes image repository used for the KubeadmControlPlane.
180197
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$

api/v1alpha1/crds/caren.nutanix.com_nutanixclusterconfigs.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,23 @@ spec:
676676
- url
677677
type: object
678678
type: array
679+
kubeProxy:
680+
description: KubeProxy defines the configuration for kube-proxy.
681+
properties:
682+
mode:
683+
default: iptables
684+
description: |-
685+
Mode specifies the mode for kube-proxy.
686+
Disabled means that kube-proxy is not installed.
687+
iptables means that kube-proxy is installed in iptables mode.
688+
enum:
689+
- Disabled
690+
- iptables
691+
type: string
692+
x-kubernetes-validations:
693+
- message: Value cannot be changed after cluster creation
694+
rule: self == oldSelf
695+
type: object
679696
kubernetesImageRepository:
680697
description: Sets the Kubernetes image repository used for the KubeadmControlPlane.
681698
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
+++
2+
title = "kube-proxy mode"
3+
+++
4+
5+
This customization allows configuration of the `kube-proxy` proxy mode. Currently, only `iptables` or `Disabled` modes
6+
are supported. `Disabled` is useful when deploying a CNI implementation that can replace `kube-proxy` to avoid
7+
potential conflicts. By default, `kube-proxy` is enabled in `iptables` mode.
8+
9+
## Example
10+
11+
To disable the deployment of `kube-proxy`, specify the following configuration:
12+
13+
```yaml
14+
apiVersion: cluster.x-k8s.io/v1beta1
15+
kind: Cluster
16+
metadata:
17+
name: <NAME>
18+
spec:
19+
topology:
20+
variables:
21+
- name: clusterConfig
22+
value:
23+
kubeProxy:
24+
mode: Disabled
25+
```
26+
27+
Applying this configuration will result in the following configuration being applied:
28+
29+
- `KubeadmControlPlaneTemplate`:
30+
31+
- ```yaml
32+
spec:
33+
template:
34+
spec:
35+
kubeadmConfigSpec:
36+
initConfiguration:
37+
skipPhases:
38+
- addon/kube-proxy
39+
```

pkg/handlers/generic/mutation/handlers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/extraapiservercertsans"
2121
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/httpproxy"
2222
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/imageregistries/credentials"
23+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubeproxymode"
2324
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/kubernetesimagerepository"
2425
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/mirrors"
2526
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/mutation/noderegistration"
@@ -44,6 +45,7 @@ func MetaMutators(mgr manager.Manager) []mutation.MetaMutator {
4445
containerdunprivilegedports.NewPatch(),
4546
encryptionatrest.NewPatch(mgr.GetClient(), encryptionatrest.RandomTokenGenerator),
4647
autorenewcerts.NewPatch(),
48+
kubeproxymode.NewPatch(),
4749

4850
// Some patches may have changed containerd configuration.
4951
// We write the configuration changes to disk, and must run a command
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2025 Nutanix. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package kubeproxymode
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12+
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
13+
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
14+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
15+
ctrl "sigs.k8s.io/controller-runtime"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
18+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
19+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
20+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
21+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
22+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
23+
)
24+
25+
const (
26+
// VariableName is the external patch variable name.
27+
VariableName = "kubeProxy"
28+
)
29+
30+
type kubeProxyMode struct {
31+
variableName string
32+
variableFieldPath []string
33+
}
34+
35+
func NewPatch() *kubeProxyMode {
36+
return newKubeProxyModePatch(
37+
v1alpha1.ClusterConfigVariableName,
38+
VariableName,
39+
"mode",
40+
)
41+
}
42+
43+
func newKubeProxyModePatch(
44+
variableName string,
45+
variableFieldPath ...string,
46+
) *kubeProxyMode {
47+
return &kubeProxyMode{
48+
variableName: variableName,
49+
variableFieldPath: variableFieldPath,
50+
}
51+
}
52+
53+
func (h *kubeProxyMode) Mutate(
54+
ctx context.Context,
55+
obj *unstructured.Unstructured,
56+
vars map[string]apiextensionsv1.JSON,
57+
holderRef runtimehooksv1.HolderReference,
58+
_ client.ObjectKey,
59+
clusterGetter mutation.ClusterGetter,
60+
) error {
61+
log := ctrl.LoggerFrom(ctx).WithValues(
62+
"holderRef", holderRef,
63+
)
64+
65+
kubeProxyMode, err := variables.Get[v1alpha1.KubeProxyMode](
66+
vars,
67+
h.variableName,
68+
h.variableFieldPath...,
69+
)
70+
if err != nil {
71+
if variables.IsNotFoundError(err) {
72+
log.V(5).Info("kubeProxy mode variable not defined")
73+
return nil
74+
}
75+
76+
return err
77+
}
78+
79+
log = log.WithValues(
80+
"variableName",
81+
h.variableName,
82+
"variableFieldPath",
83+
h.variableFieldPath,
84+
"variableValue",
85+
kubeProxyMode,
86+
)
87+
88+
if kubeProxyMode == "" {
89+
log.V(5).Info("kube proxy mode is not set, skipping mutation")
90+
return nil
91+
}
92+
93+
return patches.MutateIfApplicable(
94+
obj,
95+
vars,
96+
&holderRef,
97+
selectors.ControlPlane(),
98+
log,
99+
func(obj *controlplanev1.KubeadmControlPlaneTemplate) error {
100+
log.WithValues(
101+
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
102+
"patchedObjectName", client.ObjectKeyFromObject(obj),
103+
).Info("adding kube proxy mode to control plane kubeadm config spec")
104+
if obj.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration == nil {
105+
obj.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration = &bootstrapv1.InitConfiguration{}
106+
}
107+
108+
switch kubeProxyMode {
109+
case v1alpha1.KubeProxyModeDisabled:
110+
log.Info("kube proxy mode is set to disabled, skipping kube-proxy addon")
111+
obj.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.SkipPhases = append(
112+
obj.Spec.Template.Spec.KubeadmConfigSpec.InitConfiguration.SkipPhases,
113+
"addon/kube-proxy",
114+
)
115+
case v1alpha1.KubeProxyModeIPTables:
116+
log.Info(
117+
"kube proxy mode is set to iptables, no patches required as this is the default mode configured by kubeadm",
118+
)
119+
default:
120+
return fmt.Errorf("unknown kube proxy mode %q", kubeProxyMode)
121+
}
122+
123+
return nil
124+
},
125+
)
126+
}

0 commit comments

Comments
 (0)