From ad226782f77f4dc2d090d183c854da76725b00b4 Mon Sep 17 00:00:00 2001 From: varnastadeus Date: Fri, 20 Mar 2026 11:23:53 +0200 Subject: [PATCH 1/2] feat: add support for gw annotations --- .../networking/v1beta1/gatewayserver_types.go | 4 + .../networking.liqo.io_gatewayservers.yaml | 7 + .../server-operator/server_controller.go | 34 +++ .../server-operator/server_controller_test.go | 208 ++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go diff --git a/apis/networking/v1beta1/gatewayserver_types.go b/apis/networking/v1beta1/gatewayserver_types.go index d5de0e75fe..32d4661ae3 100644 --- a/apis/networking/v1beta1/gatewayserver_types.go +++ b/apis/networking/v1beta1/gatewayserver_types.go @@ -63,6 +63,10 @@ type GatewayServerSpec struct { // SecretRef specifies the reference to the secret containing configurations. // Leave it empty to let the operator create a new secret. SecretRef corev1.LocalObjectReference `json:"secretRef,omitempty"` + // ServiceAnnotations specifies custom annotations to be added to the service created by the gateway server. + // These annotations take precedence over any annotations defined in the server template. + // +optional + ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` } // EndpointStatus defines the observed state of the endpoint. diff --git a/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml b/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml index 9e953d1d35..10725493a1 100644 --- a/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml +++ b/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml @@ -164,6 +164,13 @@ spec: type: string type: object x-kubernetes-map-type: atomic + serviceAnnotations: + additionalProperties: + type: string + description: |- + ServiceAnnotations specifies custom annotations to be added to the service created by the gateway server. + These annotations take precedence over any annotations defined in the server template. + type: object type: object status: description: GatewayServerStatus defines the observed state of GatewayServer. diff --git a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go index 06b6a2e401..acd34c5201 100644 --- a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go +++ b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go @@ -240,6 +240,10 @@ func (r *ServerReconciler) EnsureGatewayServer(ctx context.Context, gwServer *ne if err != nil { return fmt.Errorf("unable to render the template spec: %w", err) } + + // Merge custom service annotations from GatewayServer into the rendered spec. + mergeServiceAnnotations(spec, gwServer.Spec.ServiceAnnotations) + objChild.Object["spec"] = spec return nil }) @@ -294,3 +298,33 @@ func (r *ServerReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&networkingv1beta1.GatewayServer{}). Complete(r) } + +// mergeServiceAnnotations merges the given annotations into spec.service.metadata.annotations. +// Provided annotations take precedence over existing ones in the spec. +func mergeServiceAnnotations(spec interface{}, annotations map[string]string) { + if len(annotations) == 0 { + return + } + specMap, ok := spec.(map[string]interface{}) + if !ok { + return + } + svc, _ := specMap["service"].(map[string]interface{}) + if svc == nil { + svc = map[string]interface{}{} + specMap["service"] = svc + } + meta, _ := svc["metadata"].(map[string]interface{}) + if meta == nil { + meta = map[string]interface{}{} + svc["metadata"] = meta + } + annots, _ := meta["annotations"].(map[string]interface{}) + if annots == nil { + annots = map[string]interface{}{} + } + for k, v := range annotations { + annots[k] = v + } + meta["annotations"] = annots +} diff --git a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go new file mode 100644 index 0000000000..76ff76d4a0 --- /dev/null +++ b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go @@ -0,0 +1,208 @@ +// Copyright 2019-2026 The Liqo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package serveroperator + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestServerOperator(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Server Operator Suite") +} + +var _ = Describe("mergeServiceAnnotations", func() { + type testCase struct { + spec interface{} + annotations map[string]string + expected interface{} + } + + DescribeTable("merging service annotations", + func(tc testCase) { + mergeServiceAnnotations(tc.spec, tc.annotations) + Expect(tc.spec).To(Equal(tc.expected)) + }, + + Entry("nil annotations does nothing", testCase{ + spec: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "existing": "value", + }, + }, + }, + }, + annotations: nil, + expected: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "existing": "value", + }, + }, + }, + }, + }), + + Entry("empty annotations does nothing", testCase{ + spec: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "existing": "value", + }, + }, + }, + }, + annotations: map[string]string{}, + expected: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "existing": "value", + }, + }, + }, + }, + }), + + Entry("adds annotations to existing service metadata", testCase{ + spec: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "existing": "value", + }, + }, + }, + }, + annotations: map[string]string{ + "new-key": "new-value", + }, + expected: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "existing": "value", + "new-key": "new-value", + }, + }, + }, + }, + }), + + Entry("overrides existing annotations", testCase{ + spec: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "key": "old-value", + }, + }, + }, + }, + annotations: map[string]string{ + "key": "new-value", + }, + expected: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "key": "new-value", + }, + }, + }, + }, + }), + + Entry("creates service map when missing", testCase{ + spec: map[string]interface{}{ + "deployment": map[string]interface{}{}, + }, + annotations: map[string]string{ + "key": "value", + }, + expected: map[string]interface{}{ + "deployment": map[string]interface{}{}, + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "key": "value", + }, + }, + }, + }, + }), + + Entry("creates metadata map when missing", testCase{ + spec: map[string]interface{}{ + "service": map[string]interface{}{ + "spec": map[string]interface{}{}, + }, + }, + annotations: map[string]string{ + "key": "value", + }, + expected: map[string]interface{}{ + "service": map[string]interface{}{ + "spec": map[string]interface{}{}, + "metadata": map[string]interface{}{ + "annotations": map[string]interface{}{ + "key": "value", + }, + }, + }, + }, + }), + + Entry("creates annotations map when missing", testCase{ + spec: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": map[string]interface{}{ + "app": "test", + }, + }, + }, + }, + annotations: map[string]string{ + "key": "value", + }, + expected: map[string]interface{}{ + "service": map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": map[string]interface{}{ + "app": "test", + }, + "annotations": map[string]interface{}{ + "key": "value", + }, + }, + }, + }, + }), + + Entry("non-map spec is a no-op", testCase{ + spec: "not a map", + annotations: map[string]string{"key": "value"}, + expected: "not a map", + }), + ) +}) From cc96fe17f44914ab83269f60611ef4a60e0878d8 Mon Sep 17 00:00:00 2001 From: Francesco Torta <62566275+fra98@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:41:31 +0200 Subject: [PATCH 2/2] feat: avoid TCP healthcheck on EKS GatewayServer --- .golangci.yml | 12 +- .../networking/v1beta1/gatewayserver_types.go | 4 + .../networking.liqo.io_gatewayservers.yaml | 7 + ...wireguard-gateway-server-template-eks.yaml | 256 ------------------ ...iqo-wireguard-gateway-server-template.yaml | 2 +- docs/installation/install.md | 5 +- .../server-operator/server_controller.go | 25 +- .../server-operator/server_controller_test.go | 70 ++--- pkg/liqoctl/install/eks/provider.go | 24 ++ 9 files changed, 95 insertions(+), 310 deletions(-) delete mode 100644 deployments/liqo/templates/liqo-wireguard-gateway-server-template-eks.yaml diff --git a/.golangci.yml b/.golangci.yml index 600ea38b23..0c730a5c22 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -15,7 +15,7 @@ linters: - gocyclo - godot - goheader - - gomodguard + - gomodguard_v2 - goprintffuncname - gosec - govet @@ -67,12 +67,11 @@ linters: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - gomodguard: + gomodguard_v2: blocked: - modules: - - github.com/go-logr/logr: - recommendations: - - k8s.io/klog/v2 + - module: github.com/go-logr/logr + recommendations: + - k8s.io/klog/v2 govet: enable: - shadow @@ -104,6 +103,7 @@ linters: - linters: - revive - whitespace + - goconst path: _test\.go - path: (.+)\.go$ # errcheck: Almost all programs ignore errors on these functions and in most cases it's ok diff --git a/apis/networking/v1beta1/gatewayserver_types.go b/apis/networking/v1beta1/gatewayserver_types.go index 32d4661ae3..f20354347c 100644 --- a/apis/networking/v1beta1/gatewayserver_types.go +++ b/apis/networking/v1beta1/gatewayserver_types.go @@ -67,6 +67,10 @@ type GatewayServerSpec struct { // These annotations take precedence over any annotations defined in the server template. // +optional ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` + // ServiceLabels specifies custom labels to be added to the service created by the gateway server. + // These labels take precedence over any labels defined in the server template. + // +optional + ServiceLabels map[string]string `json:"serviceLabels,omitempty"` } // EndpointStatus defines the observed state of the endpoint. diff --git a/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml b/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml index 10725493a1..e9af730e61 100644 --- a/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml +++ b/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_gatewayservers.yaml @@ -171,6 +171,13 @@ spec: ServiceAnnotations specifies custom annotations to be added to the service created by the gateway server. These annotations take precedence over any annotations defined in the server template. type: object + serviceLabels: + additionalProperties: + type: string + description: |- + ServiceLabels specifies custom labels to be added to the service created by the gateway server. + These labels take precedence over any labels defined in the server template. + type: object type: object status: description: GatewayServerStatus defines the observed state of GatewayServer. diff --git a/deployments/liqo/templates/liqo-wireguard-gateway-server-template-eks.yaml b/deployments/liqo/templates/liqo-wireguard-gateway-server-template-eks.yaml deleted file mode 100644 index 17fdf7cb62..0000000000 --- a/deployments/liqo/templates/liqo-wireguard-gateway-server-template-eks.yaml +++ /dev/null @@ -1,256 +0,0 @@ -{{- $templateConfig := (merge (dict "name" "wireguard-server" "module" "networking" "isGwTemplate" true) .) -}} -{{- $gatewayConfig := (merge (dict "name" "gateway" "module" "networking" "version" .Values.networking.gatewayTemplates.container.gateway.image.version) .) -}} -{{- $wireguardConfig := (merge (dict "name" "gateway-wireguard" "module" "networking" "version" .Values.networking.gatewayTemplates.container.wireguard.image.version) .) -}} -{{- $geneveConfig := (merge (dict "name" "gateway-geneve" "module" "networking" "version" .Values.networking.gatewayTemplates.container.geneve.image.version) .) -}} - -{{- if and .Values.networking.enabled .Values.authentication.awsConfig.accessKeyId }} - -apiVersion: networking.liqo.io/v1beta1 -kind: WgGatewayServerTemplate -metadata: - name: {{ $templateConfig.name }} - labels: - {{- include "liqo.labels" $templateConfig | nindent 4 }} -spec: - objectKind: - apiVersion: networking.liqo.io/v1beta1 - kind: WgGatewayServer - template: - metadata: - {{- include "liqo.metadataTemplate" $templateConfig | nindent 6 }} - spec: - secretRef: - name: "{{"{{ .Spec.SecretRef.Name }}"}}" - service: - metadata: - {{- include "liqo.metadataTemplate" $templateConfig | nindent 10 }} - annotations: - service.beta.kubernetes.io/aws-load-balancer-type: external - service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip - service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "80" - service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: TCP - service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: "3" - service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3" - service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "10" - service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "10" - service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing - {{- if .Values.networking.gatewayTemplates.server.service.annotations }} - {{- toYaml .Values.networking.gatewayTemplates.server.service.annotations | nindent 12 }} - {{- end }} - spec: - selector: - {{- include "liqo.selectorTemplate" (merge (dict "isService" true) $templateConfig) | nindent 12 }} - type: "{{"{{ .Spec.Endpoint.ServiceType }}"}}" - ?loadBalancerIP: "{{"{{ .Spec.Endpoint.LoadBalancerIP }}"}}" - ports: - - port: "{{"{{ .Spec.Endpoint.Port }}"}}" - protocol: UDP - targetPort: "{{"{{ .Spec.Endpoint.Port }}"}}" - ?nodePort: "{{"{{ .Spec.Endpoint.NodePort }}"}}" - {{- if .Values.networking.gatewayTemplates.server.service.allocateLoadBalancerNodePorts }} - allocateLoadBalancerNodePorts: {{ .Values.networking.gatewayTemplates.server.service.allocateLoadBalancerNodePorts }} - {{- end }} - deployment: - metadata: - {{- include "liqo.metadataTemplate" $templateConfig | nindent 10 }} - spec: - replicas: {{ .Values.networking.gatewayTemplates.replicas }} - strategy: - type: Recreate - selector: - matchLabels: - {{- include "liqo.selectorTemplate" $templateConfig | nindent 14 }} - template: - metadata: - {{- include "liqo.metadataTemplate" $templateConfig | nindent 14 }} - spec: - serviceAccount: "{{"{{ .Name }}"}}" - serviceAccountName: "{{"{{ .Name }}"}}" - {{- include "liqo.imagePullSecrets" . | nindent 14 }} - {{- if .Values.networking.gatewayTemplates.pod.tolerations }} - tolerations: - {{- toYaml .Values.networking.gatewayTemplates.pod.tolerations | nindent 14 }} - {{- end }} - containers: - - name: gateway - image: {{ .Values.networking.gatewayTemplates.container.gateway.image.name }}{{ include "liqo.suffix" $gatewayConfig }}:{{ include "liqo.version" $gatewayConfig }} - imagePullPolicy: {{ .Values.pullPolicy }} - args: - - --name={{"{{ .Name }}"}} - - --namespace={{"{{ .Namespace }}"}} - - --remote-cluster-id={{"{{ .ClusterID }}"}} - - --node-name={{"$(NODE_NAME)"}} - - --pod-name={{"$(POD_NAME)"}} - - --gateway-uid={{"{{ .GatewayUID }}"}} - - --mode=server - - --container-name=gateway - - --concurrent-containers-names=wireguard,geneve - {{- if .Values.common.globalAnnotations }} - {{- $d := dict "commandName" "--global-annotations" "dictionary" .Values.common.globalAnnotations -}} - {{- include "liqo.concatenateMap" $d | nindent 16 }} - {{- end }} - {{- if .Values.common.globalLabels }} - {{- $d := dict "commandName" "--global-labels" "dictionary" .Values.common.globalLabels -}} - {{- include "liqo.concatenateMap" $d | nindent 16 }} - {{- end }} - {{- if .Values.metrics.enabled }} - - --metrics-address=:8082 - {{- end }} - - --health-probe-bind-address=:8083 - - --ping-enabled=true - - --ping-loss-threshold={{ .Values.networking.gatewayTemplates.ping.lossThreshold }} - - --ping-interval={{ .Values.networking.gatewayTemplates.ping.interval }} - - --ping-update-status-interval={{ .Values.networking.gatewayTemplates.ping.updateStatusInterval }} - {{- if gt (int .Values.networking.gatewayTemplates.replicas) 1 }} - - --leader-election=true - {{- else }} - - --leader-election=false - {{- end }} - {{- if not .Values.requirements.kernel.enabled }} - - --disable-kernel-version-check - {{- end }} - - --enable-nft-monitor={{ .Values.networking.gatewayTemplates.nftablesMonitor }} - - --enable-route-monitor={{ .Values.networking.gatewayTemplates.routeMonitor }} - volumeMounts: - - name: ipc - mountPath: /ipc - ports: - {{- if .Values.metrics.enabled }} - - containerPort: 8082 - name: gw-metrics - {{- end }} - - containerPort: 8083 - name: healthz - # ATTENTION: uncomment the readinessProbe section if you are aware of the consequences. - # If you have more replicas of the same gateway, the passive ones will not reach the ready state. - #readinessProbe: - # httpGet: - # path: /readyz - # port: healthz - env: - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - securityContext: - privileged: true - capabilities: - add: - - NET_ADMIN - - NET_RAW - - name: wireguard - image: {{ .Values.networking.gatewayTemplates.container.wireguard.image.name }}{{ include "liqo.suffix" $wireguardConfig }}:{{ include "liqo.version" $wireguardConfig }} - imagePullPolicy: {{ .Values.pullPolicy }} - args: - - --name={{"{{ .Name }}"}} - - --namespace={{"{{ .Namespace }}"}} - - --remote-cluster-id={{"{{ .ClusterID }}"}} - - --gateway-uid={{"{{ .GatewayUID }}"}} - - --mode=server - - --container-name=wireguard - - --mtu={{"{{ .Spec.MTU }}"}} - - --listen-port={{"{{ .Spec.Endpoint.Port }}"}} - {{- if .Values.metrics.enabled }} - - --metrics-address=:8084 - {{- end }} - - --health-probe-bind-address=:8085 - {{- if gt (int .Values.networking.gatewayTemplates.replicas) 1 }} - - --leader-election=true - {{- else }} - - --leader-election=false - {{- end }} - - --implementation={{ .Values.networking.gatewayTemplates.wireguard.implementation }} - ports: - {{- if .Values.metrics.enabled }} - - containerPort: 8084 - name: wg-metrics - {{- end }} - - containerPort: 8085 - name: healthz - # ATTENTION: uncomment the readinessProbe section if you are aware of the consequences. - # If you have more replicas of the same gateway, the passive ones will not reach the ready state. - #readinessProbe: - # httpGet: - # path: /readyz - # port: healthz - securityContext: - capabilities: - add: - - NET_ADMIN - - NET_RAW - {{ if .Values.networking.gatewayTemplates.wireguard.implementation | eq "userspace" }} - privileged: true - {{ end }} - volumeMounts: - - name: ipc - mountPath: /ipc - - name: wireguard-config - mountPath: /etc/wireguard/keys - - name: geneve - image: {{ .Values.networking.gatewayTemplates.container.geneve.image.name }}{{ include "liqo.suffix" $geneveConfig }}:{{ include "liqo.version" $geneveConfig }} - imagePullPolicy: {{ .Values.pullPolicy }} - args: - - --name={{"{{ .Name }}"}} - - --namespace={{"{{ .Namespace }}"}} - - --remote-cluster-id={{"{{ .ClusterID }}"}} - - --node-name={{"$(NODE_NAME)"}} - - --pod-name={{"$(POD_NAME)"}} - - --gateway-uid={{"{{ .GatewayUID }}"}} - - --mode=server - - --container-name=geneve - - --geneve-port={{ .Values.networking.genevePort }} - {{- if .Values.metrics.enabled }} - - --metrics-address=:8086 - {{- end }} - - --health-probe-bind-address=:8087 - {{- if gt (int .Values.networking.gatewayTemplates.replicas) 1 }} - - --leader-election=true - {{- else }} - - --leader-election=false - {{- end }} - volumeMounts: - - name: ipc - mountPath: /ipc - ports: - {{- if .Values.metrics.enabled }} - - containerPort: 8086 - name: gv-metrics - {{- end }} - - containerPort: 8087 - name: healthz - # ATTENTION: uncomment the readinessProbe section if you are aware of the consequences. - # If you have more replicas of the same gateway, the passive ones will not reach the ready state. - #readinessProbe: - # httpGet: - # path: /readyz - # port: healthz - env: - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - securityContext: - capabilities: - add: - - NET_ADMIN - - NET_RAW - - name: tcp-healthcheck - image: nginx - {{- if .Values.networking.gatewayTemplates.pod.priorityClassName }} - priorityClassName: {{ .Values.networking.gatewayTemplates.pod.priorityClassName }} - {{- end }} - volumes: - - name: wireguard-config - secret: - secretName: "{{"{{ .SecretName }}"}}" - - name: ipc - emptyDir: {} -{{- end }} diff --git a/deployments/liqo/templates/liqo-wireguard-gateway-server-template.yaml b/deployments/liqo/templates/liqo-wireguard-gateway-server-template.yaml index a7208918b4..ccc065cf97 100644 --- a/deployments/liqo/templates/liqo-wireguard-gateway-server-template.yaml +++ b/deployments/liqo/templates/liqo-wireguard-gateway-server-template.yaml @@ -3,7 +3,7 @@ {{- $wireguardConfig := (merge (dict "name" "gateway-wireguard" "module" "networking" "version" .Values.networking.gatewayTemplates.container.wireguard.image.version) .) -}} {{- $geneveConfig := (merge (dict "name" "gateway-geneve" "module" "networking" "version" .Values.networking.gatewayTemplates.container.geneve.image.version) .) -}} -{{- if and .Values.networking.enabled (not .Values.authentication.awsConfig.accessKeyId) }} +{{- if .Values.networking.enabled }} apiVersion: networking.liqo.io/v1beta1 kind: WgGatewayServerTemplate diff --git a/docs/installation/install.md b/docs/installation/install.md index c6685d43fb..132faa123c 100644 --- a/docs/installation/install.md +++ b/docs/installation/install.md @@ -120,7 +120,10 @@ Liqo does NOT support: ``` ```{admonition} Note -If you are planning to use an EKS cluster as [network server](/advanced/peering/inter-cluster-network), you need to install the [AWS Load Balancer V2 Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.8/) on the EKS cluster. +If you are planning to use an EKS cluster as [network server](/advanced/peering/inter-cluster-network), it is highly recommended to install the [AWS Load Balancer V2 Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.8/) on the EKS cluster. +If you cannot install it and you plan to use the legacy in-tree AWS Load Balancer, make sure to pass the following annotations to the gateway service through the `networking.gatewayTemplates.server.service.annotations` helm value or using the Gatewayserver CR under `.spec.serviceAnnotations`: +- `service.beta.kubernetes.io/aws-load-balancer-type: "nlb"` +- `service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled`: "true" ``` **Supported CNIs** diff --git a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go index acd34c5201..5f52f76f76 100644 --- a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go +++ b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller.go @@ -241,8 +241,9 @@ func (r *ServerReconciler) EnsureGatewayServer(ctx context.Context, gwServer *ne return fmt.Errorf("unable to render the template spec: %w", err) } - // Merge custom service annotations from GatewayServer into the rendered spec. - mergeServiceAnnotations(spec, gwServer.Spec.ServiceAnnotations) + // Merge custom service annotations and labels from GatewayServer into the rendered spec. + mergeServiceMetadataField(spec, "annotations", gwServer.Spec.ServiceAnnotations) + mergeServiceMetadataField(spec, "labels", gwServer.Spec.ServiceLabels) objChild.Object["spec"] = spec return nil @@ -299,10 +300,10 @@ func (r *ServerReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// mergeServiceAnnotations merges the given annotations into spec.service.metadata.annotations. -// Provided annotations take precedence over existing ones in the spec. -func mergeServiceAnnotations(spec interface{}, annotations map[string]string) { - if len(annotations) == 0 { +// mergeServiceMetadataField merges the given key-value pairs into spec.service.metadata.. +// Provided values take precedence over existing ones in the spec. +func mergeServiceMetadataField(spec interface{}, field string, values map[string]string) { + if len(values) == 0 { return } specMap, ok := spec.(map[string]interface{}) @@ -319,12 +320,12 @@ func mergeServiceAnnotations(spec interface{}, annotations map[string]string) { meta = map[string]interface{}{} svc["metadata"] = meta } - annots, _ := meta["annotations"].(map[string]interface{}) - if annots == nil { - annots = map[string]interface{}{} + existing, _ := meta[field].(map[string]interface{}) + if existing == nil { + existing = map[string]interface{}{} } - for k, v := range annotations { - annots[k] = v + for k, v := range values { + existing[k] = v } - meta["annotations"] = annots + meta[field] = existing } diff --git a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go index 76ff76d4a0..318f9d40fe 100644 --- a/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go +++ b/pkg/liqo-controller-manager/networking/external-network/server-operator/server_controller_test.go @@ -26,34 +26,36 @@ func TestServerOperator(t *testing.T) { RunSpecs(t, "Server Operator Suite") } -var _ = Describe("mergeServiceAnnotations", func() { +var _ = Describe("mergeServiceMetadataField", func() { type testCase struct { - spec interface{} - annotations map[string]string - expected interface{} + spec interface{} + values map[string]string + expected interface{} } - DescribeTable("merging service annotations", + const field = "annotations" + + DescribeTable("merging service metadata field", func(tc testCase) { - mergeServiceAnnotations(tc.spec, tc.annotations) + mergeServiceMetadataField(tc.spec, field, tc.values) Expect(tc.spec).To(Equal(tc.expected)) }, - Entry("nil annotations does nothing", testCase{ + Entry("nil values does nothing", testCase{ spec: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "existing": "value", }, }, }, }, - annotations: nil, + values: nil, expected: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "existing": "value", }, }, @@ -61,21 +63,21 @@ var _ = Describe("mergeServiceAnnotations", func() { }, }), - Entry("empty annotations does nothing", testCase{ + Entry("empty values does nothing", testCase{ spec: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "existing": "value", }, }, }, }, - annotations: map[string]string{}, + values: map[string]string{}, expected: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "existing": "value", }, }, @@ -83,23 +85,23 @@ var _ = Describe("mergeServiceAnnotations", func() { }, }), - Entry("adds annotations to existing service metadata", testCase{ + Entry("adds values to existing service metadata", testCase{ spec: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "existing": "value", }, }, }, }, - annotations: map[string]string{ + values: map[string]string{ "new-key": "new-value", }, expected: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "existing": "value", "new-key": "new-value", }, @@ -108,23 +110,23 @@ var _ = Describe("mergeServiceAnnotations", func() { }, }), - Entry("overrides existing annotations", testCase{ + Entry("overrides existing values", testCase{ spec: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "key": "old-value", }, }, }, }, - annotations: map[string]string{ + values: map[string]string{ "key": "new-value", }, expected: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "key": "new-value", }, }, @@ -136,14 +138,14 @@ var _ = Describe("mergeServiceAnnotations", func() { spec: map[string]interface{}{ "deployment": map[string]interface{}{}, }, - annotations: map[string]string{ + values: map[string]string{ "key": "value", }, expected: map[string]interface{}{ "deployment": map[string]interface{}{}, "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "key": "value", }, }, @@ -157,14 +159,14 @@ var _ = Describe("mergeServiceAnnotations", func() { "spec": map[string]interface{}{}, }, }, - annotations: map[string]string{ + values: map[string]string{ "key": "value", }, expected: map[string]interface{}{ "service": map[string]interface{}{ "spec": map[string]interface{}{}, "metadata": map[string]interface{}{ - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "key": "value", }, }, @@ -172,26 +174,26 @@ var _ = Describe("mergeServiceAnnotations", func() { }, }), - Entry("creates annotations map when missing", testCase{ + Entry("creates field map when missing", testCase{ spec: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "labels": map[string]interface{}{ + "other": map[string]interface{}{ "app": "test", }, }, }, }, - annotations: map[string]string{ + values: map[string]string{ "key": "value", }, expected: map[string]interface{}{ "service": map[string]interface{}{ "metadata": map[string]interface{}{ - "labels": map[string]interface{}{ + "other": map[string]interface{}{ "app": "test", }, - "annotations": map[string]interface{}{ + field: map[string]interface{}{ "key": "value", }, }, @@ -200,9 +202,9 @@ var _ = Describe("mergeServiceAnnotations", func() { }), Entry("non-map spec is a no-op", testCase{ - spec: "not a map", - annotations: map[string]string{"key": "value"}, - expected: "not a map", + spec: "not a map", + values: map[string]string{"key": "value"}, + expected: "not a map", }), ) }) diff --git a/pkg/liqoctl/install/eks/provider.go b/pkg/liqoctl/install/eks/provider.go index 24a3b4a996..263cbc373f 100644 --- a/pkg/liqoctl/install/eks/provider.go +++ b/pkg/liqoctl/install/eks/provider.go @@ -130,6 +130,30 @@ func (o *Options) Values() map[string]interface{} { "fullMasquerade": true, }, }, + "gatewayTemplates": map[string]interface{}{ + "server": map[string]interface{}{ + "service": map[string]interface{}{ + "externalTrafficPolicy": "Cluster", + "annotations": map[string]interface{}{ + // Annots for the AWS LoadBalancer Controller: https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/service/annotations/ + "service.beta.kubernetes.io/aws-load-balancer-type": "external", + "service.beta.kubernetes.io/aws-load-balancer-scheme": "internet-facing", + "service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "ip", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": "8083", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol": "HTTP", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-path": "/healthz", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold": "2", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold": "3", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout": "10", + "service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval": "10", + // Annots for the legacy in-tree AWS load balancer (not recommended): + // "service.beta.kubernetes.io/aws-load-balancer-type": "nlb", + // "service.beta.kubernetes.io/aws-load-balancer-scheme": "internet-facing", + // "service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true", + }, + }, + }, + }, }, "authentication": map[string]interface{}{