diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/virtual-ip/kube-vip/manifests/kube-vip-configmap.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/virtual-ip/kube-vip/manifests/kube-vip-configmap.yaml index f0be06cd5..3c210aa11 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/virtual-ip/kube-vip/manifests/kube-vip-configmap.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/virtual-ip/kube-vip/manifests/kube-vip-configmap.yaml @@ -22,7 +22,7 @@ data: - name: vip_arp value: "true" - name: port - value: '{{ `{{ .ControlPlaneEndpoint.Port }}` }}' + value: '{{ `{{ .Port }}` }}' - name: vip_nodename valueFrom: fieldRef: @@ -46,7 +46,7 @@ data: - name: vip_retryperiod value: "2" - name: address - value: '{{ `{{ .ControlPlaneEndpoint.Host }}` }}' + value: '{{ `{{ .Address }}` }}' - name: prometheus_server image: ghcr.io/kube-vip/kube-vip:v0.8.3 imagePullPolicy: IfNotPresent diff --git a/docs/content/customization/nutanix/control-plane-endpoint.md b/docs/content/customization/nutanix/control-plane-endpoint.md index f28c15441..4a5629265 100644 --- a/docs/content/customization/nutanix/control-plane-endpoint.md +++ b/docs/content/customization/nutanix/control-plane-endpoint.md @@ -2,7 +2,7 @@ title = "Control Plane Endpoint" +++ -Configure Control Plane Endpoint. Defines the host IP and port of the CAPX Kubernetes cluster. +Configure Control Plane Endpoint. Defines the host IP and port of the Nutanix Kubernetes cluster. ## Examples @@ -51,6 +51,93 @@ spec: name: kube-vip namespace: kube-system spec: + containers: + - name: kube-vip + args: + - manager + env: + - name: port + value: '6443' + - name: address + value: 'x.x.x.x' + ... + owner: root:root + path: /etc/kubernetes/manifests/kube-vip.yaml + permissions: "0600" + postKubeadmCommands: + # Only added for clusters version >=v1.29.0 + - |- + if [ -f /run/kubeadm/kubeadm.yaml ]; then + sed -i 's#path: /etc/kubernetes/super-admin.conf#path: ... + fi + preKubeadmCommands: + # Only added for clusters version >=v1.29.0 + - |- + if [ -f /run/kubeadm/kubeadm.yaml ]; then + sed -i 's#path: /etc/kubernetes/admin.conf#path: ... + fi +``` + +### Set Control Plane Endpoint and a Different Virtual IP + +It is also possible to set a separate virtual IP to be used by kube-vip from the control plane endpoint. +This is useful in VPC setups or other instances +when you have an external floating IP already associated with the virtual IP. + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + nutanix: + controlPlaneEndpoint: + host: x.x.x.x + port: 6443 + virtualIP: + configuration: + address: y.y.y.y +``` + +Applying this configuration will result in the following value being set: + +- `NutanixCluster`: + +```yaml +spec: + template: + spec: + controlPlaneEndpoint: + host: x.x.x.x + port: 6443 +``` + +- `KubeadmControlPlaneTemplate` + +```yaml + spec: + kubeadmConfigSpec: + files: + - content: | + apiVersion: v1 + kind: Pod + metadata: + name: kube-vip + namespace: kube-system + spec: + containers: + - name: kube-vip + args: + - manager + env: + - name: port + value: '6443' + - name: address + value: 'y.y.y.y' ... owner: root:root path: /etc/kubernetes/manifests/kube-vip.yaml diff --git a/hack/addons/update-kube-vip-manifests.sh b/hack/addons/update-kube-vip-manifests.sh index 29dd54eff..45116c697 100755 --- a/hack/addons/update-kube-vip-manifests.sh +++ b/hack/addons/update-kube-vip-manifests.sh @@ -33,8 +33,8 @@ docker container run --rm ghcr.io/kube-vip/kube-vip:"${KUBE_VIP_VERSION}" \ gojq --yaml-input --yaml-output \ 'del(.metadata.creationTimestamp, .status) | .spec.containers[].imagePullPolicy |= "IfNotPresent" | - (.spec.containers[0].env[] | select(.name == "port").value) |= "{{ `{{ .ControlPlaneEndpoint.Port }}` }}" | - (.spec.containers[0].env[] | select(.name == "address").value) |= "{{ `{{ .ControlPlaneEndpoint.Host }}` }}" + (.spec.containers[0].env[] | select(.name == "port").value) |= "{{ `{{ .Port }}` }}" | + (.spec.containers[0].env[] | select(.name == "address").value) |= "{{ `{{ .Address }}` }}" ' >"${ASSETS_DIR}/${FILE_NAME}" kubectl create configmap "{{ .Values.hooks.virtualIP.kubeVip.defaultTemplateConfigMap.name }}" --dry-run=client --output yaml \ diff --git a/pkg/handlers/generic/mutation/controlplanevirtualip/inject_test.go b/pkg/handlers/generic/mutation/controlplanevirtualip/inject_test.go index ff242a1be..21adcd764 100644 --- a/pkg/handlers/generic/mutation/controlplanevirtualip/inject_test.go +++ b/pkg/handlers/generic/mutation/controlplanevirtualip/inject_test.go @@ -264,7 +264,7 @@ spec: - name: vip_arp value: "true" - name: address - value: "{{ .ControlPlaneEndpoint.Host }}" + value: "{{ .Address }}" - name: port - value: "{{ .ControlPlaneEndpoint.Port }}" + value: "{{ .Port }}" ` diff --git a/pkg/handlers/generic/mutation/controlplanevirtualip/providers/kubevip_test.go b/pkg/handlers/generic/mutation/controlplanevirtualip/providers/kubevip_test.go index 312d06afa..a2fef1967 100644 --- a/pkg/handlers/generic/mutation/controlplanevirtualip/providers/kubevip_test.go +++ b/pkg/handlers/generic/mutation/controlplanevirtualip/providers/kubevip_test.go @@ -105,6 +105,79 @@ func Test_GenerateFilesAndCommands(t *testing.T) { }, }, }, + { + name: "should return templated data with both IP and port from virtual IP configuration overrides", + controlPlaneEndpointSpec: v1alpha1.ControlPlaneEndpointSpec{ + Host: "10.20.100.10", + Port: 6443, + VirtualIPSpec: &v1alpha1.ControlPlaneVirtualIPSpec{ + Configuration: &v1alpha1.ControlPlaneVirtualIPConfiguration{ + Address: "172.20.100.10", + Port: 8443, + }, + }, + }, + configMap: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-kube-vip-template", + Namespace: "default", + }, + Data: map[string]string{ + "data": validKubeVIPTemplate, + }, + }, + cluster: &clusterv1.Cluster{ + Spec: clusterv1.ClusterSpec{ + Topology: &clusterv1.Topology{ + Version: "v1.28.0", + }, + }, + }, + expectedFiles: []bootstrapv1.File{ + { + Content: expectedKubeVIPPodWithOverrides, + Owner: kubeVIPFileOwner, + Path: kubeVIPFilePath, + Permissions: kubeVIPFilePermissions, + }, + }, + }, + { + name: "should return templated data with IP from virtual IP configuration overrides", + controlPlaneEndpointSpec: v1alpha1.ControlPlaneEndpointSpec{ + Host: "10.20.100.10", + Port: 8443, + VirtualIPSpec: &v1alpha1.ControlPlaneVirtualIPSpec{ + Configuration: &v1alpha1.ControlPlaneVirtualIPConfiguration{ + Address: "172.20.100.10", + }, + }, + }, + configMap: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default-kube-vip-template", + Namespace: "default", + }, + Data: map[string]string{ + "data": validKubeVIPTemplate, + }, + }, + cluster: &clusterv1.Cluster{ + Spec: clusterv1.ClusterSpec{ + Topology: &clusterv1.Topology{ + Version: "v1.28.0", + }, + }, + }, + expectedFiles: []bootstrapv1.File{ + { + Content: expectedKubeVIPPodWithOverrides, + Owner: kubeVIPFileOwner, + Path: kubeVIPFilePath, + Permissions: kubeVIPFilePermissions, + }, + }, + }, } for idx := range tests { @@ -227,9 +300,9 @@ spec: - name: vip_arp value: "true" - name: address - value: "{{ .ControlPlaneEndpoint.Host }}" + value: "{{ .Address }}" - name: port - value: "{{ .ControlPlaneEndpoint.Port }}" + value: "{{ .Port }}" ` expectedKubeVIPPod = ` @@ -253,4 +326,26 @@ spec: - name: port value: "6443" ` + + expectedKubeVIPPodWithOverrides = ` +apiVersion: v1 +kind: Pod +metadata: + name: kube-vip + namespace: kube-system +spec: + containers: + - name: kube-vip + image: ghcr.io/kube-vip/kube-vip:v1.1.1 + imagePullPolicy: IfNotPresent + args: + - manager + env: + - name: vip_arp + value: "true" + - name: address + value: "172.20.100.10" + - name: port + value: "8443" +` ) diff --git a/pkg/handlers/generic/mutation/controlplanevirtualip/providers/providers.go b/pkg/handlers/generic/mutation/controlplanevirtualip/providers/providers.go index 1a8564326..788532dac 100644 --- a/pkg/handlers/generic/mutation/controlplanevirtualip/providers/providers.go +++ b/pkg/handlers/generic/mutation/controlplanevirtualip/providers/providers.go @@ -5,6 +5,7 @@ package providers import ( "bytes" + "cmp" "context" "fmt" "text/template" @@ -35,11 +36,28 @@ func templateValues( } type input struct { - ControlPlaneEndpoint v1alpha1.ControlPlaneEndpointSpec + Address string + Port int32 } + // If specified, use the virtual IP address and/or port, + // otherwise fall back to the control plane endpoint host and port. + address := controlPlaneEndpoint.Host + port := controlPlaneEndpoint.Port + if controlPlaneEndpoint.VirtualIPSpec != nil && + controlPlaneEndpoint.VirtualIPSpec.Configuration != nil { + address = cmp.Or( + controlPlaneEndpoint.VirtualIPSpec.Configuration.Address, + controlPlaneEndpoint.Host, + ) + port = cmp.Or( + controlPlaneEndpoint.VirtualIPSpec.Configuration.Port, + controlPlaneEndpoint.Port, + ) + } templateInput := input{ - ControlPlaneEndpoint: controlPlaneEndpoint, + Address: address, + Port: port, } var b bytes.Buffer