mirror of
https://github.com/clastix/kamaji.git
synced 2026-08-26 00:47:20 +00:00
fix(gateway): allow explicit parentRefs for kube-apiserver TLSRoute (#1074)
Previously, when Kamaji created TLSRoutes for the kube-apiserver and konnectivity, it automatically set the parentRefs `port` and `sectionName`, overriding any user-provided values via TCP spec. This prevented users from targeting specific Gateway listeners. This change allows users to explicitly define parentRefs for the kube-apiserver TLSRoute through `TCP.spec.controlPlane.gateway.parentRefs`. The konnectivity TLSRoute behavior remains unchanged. Signed-off-by: Parth Yadav <parth@coredge.io>
This commit is contained in:
@@ -155,7 +155,6 @@ type IngressSpec struct {
|
||||
}
|
||||
|
||||
// GatewaySpec defines the options for the Gateway which will expose API Server of the Tenant Control Plane.
|
||||
// +kubebuilder:validation:XValidation:rule="!has(self.parentRefs) || size(self.parentRefs) == 0 || self.parentRefs.all(ref, !has(ref.port) && !has(ref.sectionName))",message="parentRefs must not specify port or sectionName, these are set automatically by Kamaji"
|
||||
type GatewaySpec struct {
|
||||
// AdditionalMetadata to add Labels and Annotations support.
|
||||
AdditionalMetadata AdditionalMetadata `json:"additionalMetadata,omitempty"`
|
||||
|
||||
@@ -6896,9 +6896,6 @@ versions:
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: parentRefs must not specify port or sectionName, these are set automatically by Kamaji
|
||||
rule: '!has(self.parentRefs) || size(self.parentRefs) == 0 || self.parentRefs.all(ref, !has(ref.port) && !has(ref.sectionName))'
|
||||
ingress:
|
||||
description: Defining the options for an Optional Ingress which will expose API Server of the Tenant Control Plane
|
||||
properties:
|
||||
|
||||
@@ -6904,9 +6904,6 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: parentRefs must not specify port or sectionName, these are set automatically by Kamaji
|
||||
rule: '!has(self.parentRefs) || size(self.parentRefs) == 0 || self.parentRefs.all(ref, !has(ref.port) && !has(ref.sectionName))'
|
||||
ingress:
|
||||
description: Defining the options for an Optional Ingress which will expose API Server of the Tenant Control Plane
|
||||
properties:
|
||||
|
||||
+130
-112
@@ -1,106 +1,34 @@
|
||||
# Gateway API Support
|
||||
# Gateway API
|
||||
|
||||
Kamaji provides built-in support for the [Gateway API](https://gateway-api.sigs.k8s.io/), allowing you to expose Tenant Control Planes using TLSRoute resources with SNI-based routing. This enables hostname-based routing to multiple Tenant Control Planes through a single Gateway resource, reducing the need for dedicated LoadBalancer services.
|
||||
Kamaji provides built-in support for the [Gateway API](https://gateway-api.sigs.k8s.io/), allowing Tenant Control Planes to be exposed as SNI-based addresses/urls. This eliminates the need for a dedicated LoadBalancer IP per TCP. A single Gateway resource can be used for multiple Tenant Control Planes and provide access to them with hostname-based routing (like `https://mycluster.xyz.com:6443`).
|
||||
|
||||
## Overview
|
||||
You can configure Gateway in Tenant Control Plane via `tcp.spec.controlPlane.gateway`, Kamaji will automatically create a `TLSRoute` resource with corresponding spec. To make this configuration work, you need to ensure `gateway` exists (is created by you) and `tcp.spec.controlPlane.gateway` points to your gateway.
|
||||
|
||||
Gateway API support in Kamaji automatically creates and manages TLSRoute resources for your Tenant Control Planes. When you configure a Gateway for a Tenant Control Plane, Kamaji automatically creates TLSRoutes for the Control Plane API Server. If konnectivity is enabled, a separate TLSRoute is created for it. Both TLSRoutes use the same hostname and Gateway resource, but route to different ports(listeners) using port-based routing and semantic `sectionName` values.
|
||||
We will cover a few examples below on how this is done.
|
||||
|
||||
Therefore, the target `Gateway` resource must have right listener configurations (see the Gateway [example section](#gateway-resource-setup) below).
|
||||
|
||||
|
||||
## How It Works
|
||||
|
||||
When you configure `spec.controlPlane.gateway` in a TenantControlPlane resource, Kamaji automatically:
|
||||
|
||||
1. **Creates a TLSRoute for the control plane** that routes for port 6443 (or `spec.networkProfile.port`) with sectionName `"kube-apiserver"`
|
||||
2. **Creates a TLSRoute for Konnectivity** (if konnectivity addon is enabled) that routes for port 8132 (or `spec.addons.konnectivity.server.port`) with sectionName `"konnectivity-server"`
|
||||
|
||||
Both TLSRoutes:
|
||||
|
||||
- Use the same hostname from `spec.controlPlane.gateway.hostname`
|
||||
- Reference the same parent Gateway resource via `parentRefs`
|
||||
- The `port` and `sectionName` fields are set automatically by Kamaji
|
||||
- Route to the appropriate Tenant Control Plane service
|
||||
|
||||
The Gateway resource must have listeners configured for both ports (6443 and 8132) to support both routes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using Gateway API support, ensure:
|
||||
Before using Gateway API mode, please ensure:
|
||||
|
||||
1. **Gateway API CRDs are installed** in your cluster (Required CRDs: `GatewayClass`, `Gateway`, `TLSRoute`)
|
||||
|
||||
2. **A Gateway resource exists** with appropriate listeners configured:
|
||||
- At minimum, listeners for ports 6443 (control plane) and 8132 (Konnectivity)
|
||||
- TLS protocol with Passthrough mode
|
||||
- Hostname pattern matching your Tenant Control Plane hostnames
|
||||
2. **A Gateway resource exists** with appropriate configuration (see examples in this guide):
|
||||
- Listeners for kube-apiserver.
|
||||
- Use TLS protocol with Passthrough mode
|
||||
- Hostname (or Hostname pattern) matching your Tenant Control Plane hostname
|
||||
|
||||
3. **DNS is configured** to resolve your hostnames to the Gateway's external address
|
||||
3. (optional) **DNS is configured** to resolve hostnames (or hostname pattern) to the Gateway's LoadBalancer IP address. (This is needed for worker nodes to join, for testing we will use host entries in `/etc/hosts` for this guide)
|
||||
|
||||
4. **Gateway controller is running** (e.g., Envoy Gateway, Istio Gateway, etc.)
|
||||
|
||||
## Configuration
|
||||
To replicate the guide below, please install [Envoy Gateway](https://gateway.envoyproxy.io/docs/tasks/quickstart/).
|
||||
|
||||
### TenantControlPlane Gateway Configuration
|
||||
Next, create a gateway resource:
|
||||
|
||||
Enable Gateway API mode by setting the `spec.controlPlane.gateway` field in your TenantControlPlane resource:
|
||||
#### Gateway Resource Setup
|
||||
|
||||
```yaml
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: TenantControlPlane
|
||||
metadata:
|
||||
name: tcp-1
|
||||
spec:
|
||||
controlPlane:
|
||||
# ... gateway configuration:
|
||||
gateway:
|
||||
hostname: "tcp1.cluster.dev"
|
||||
parentRefs:
|
||||
- name: gateway
|
||||
namespace: default
|
||||
additionalMetadata:
|
||||
labels:
|
||||
environment: production
|
||||
annotations:
|
||||
example.com/custom: "value"
|
||||
# ... rest of the spec
|
||||
deployment:
|
||||
replicas: 1
|
||||
service:
|
||||
serviceType: ClusterIP
|
||||
dataStore: default
|
||||
kubernetes:
|
||||
version: v1.29.0
|
||||
kubelet:
|
||||
cgroupfs: systemd
|
||||
networkProfile:
|
||||
port: 6443
|
||||
certSANs:
|
||||
- "c11.cluster.dev" # make sure to set this.
|
||||
addons:
|
||||
coreDNS: {}
|
||||
kubeProxy: {}
|
||||
konnectivity: {}
|
||||
|
||||
```
|
||||
|
||||
**Required fields:**
|
||||
|
||||
- `hostname`: The hostname that will be used for routing (must match Gateway listener hostname pattern)
|
||||
- `parentRefs`: Array of Gateway references (name and namespace)
|
||||
|
||||
**Optional fields:**
|
||||
|
||||
- `additionalMetadata.labels`: Custom labels to add to TLSRoute resources
|
||||
- `additionalMetadata.annotations`: Custom annotations to add to TLSRoute resources
|
||||
|
||||
!!! warning "Port and sectionName are set automatically"
|
||||
Do not specify `port` or `sectionName` in `parentRefs`. Kamaji automatically sets these fields in TLSRoutes.
|
||||
|
||||
### Gateway Resource Setup
|
||||
|
||||
Your Gateway resource must have listeners configured for both the control plane and Konnectivity ports. Here's an example Gateway configuration:
|
||||
Your Gateway resource must have listeners configured for the control plane. Here's an example Gateway configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
@@ -130,27 +58,94 @@ spec:
|
||||
kind: TLSRoute
|
||||
namespaces:
|
||||
from: All
|
||||
```
|
||||
The above gateway is configured with envoy gateway controller. You can achieve the same results with any other gateway controller that supports `TLSRoutes` and TLS passthrough mode.
|
||||
|
||||
The rest of this guide focuses on TCP.
|
||||
|
||||
## TenantControlPlane Gateway Configuration
|
||||
|
||||
Enable Gateway API mode by setting the `spec.controlPlane.gateway` field in your TenantControlPlane resource:
|
||||
|
||||
```yaml
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: TenantControlPlane
|
||||
metadata:
|
||||
name: tcp-1
|
||||
spec:
|
||||
controlPlane:
|
||||
|
||||
# ... gateway configuration:
|
||||
gateway:
|
||||
hostname: "tcp1.cluster.dev"
|
||||
parentRefs:
|
||||
- name: gateway
|
||||
namespace: default
|
||||
sectionName: kube-apiserver
|
||||
port: 6443
|
||||
additionalMetadata:
|
||||
labels:
|
||||
environment: production
|
||||
annotations:
|
||||
example.com/custom: "value"
|
||||
|
||||
# ... rest of the spec
|
||||
deployment:
|
||||
replicas: 1
|
||||
service:
|
||||
serviceType: ClusterIP
|
||||
dataStore: default
|
||||
kubernetes:
|
||||
version: v1.29.0
|
||||
kubelet:
|
||||
cgroupfs: systemd
|
||||
networkProfile:
|
||||
port: 6443
|
||||
certSANs:
|
||||
- "tcp1.cluster.dev" # make sure to set this.
|
||||
addons:
|
||||
coreDNS: {}
|
||||
kubeProxy: {}
|
||||
|
||||
# if konnectivity addon is enabled:
|
||||
- name: konnectivity-server
|
||||
port: 8132
|
||||
protocol: TLS
|
||||
hostname: 'tcp1.cluster.dev'
|
||||
tls:
|
||||
mode: Passthrough
|
||||
allowedRoutes:
|
||||
kinds:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: TLSRoute
|
||||
namespaces:
|
||||
from: All
|
||||
|
||||
```
|
||||
|
||||
**Required fields:**
|
||||
|
||||
## Multiple Tenant Control Planes
|
||||
- `hostname`: The hostname that will be used for routing (must match Gateway listener hostname pattern)
|
||||
- `parentRefs`: Array of Gateway references
|
||||
|
||||
You can use the same Gateway resource for multiple Tenant Control Planes by using different hostnames:
|
||||
**Optional fields:**
|
||||
|
||||
- `additionalMetadata.labels`: Custom labels to add to TLSRoute resources
|
||||
- `additionalMetadata.annotations`: Custom annotations to add to TLSRoute resources
|
||||
|
||||
!!! info
|
||||
### Verify
|
||||
|
||||
From our kubectl client machine / remote machines we can access the cluster above with the hostname.
|
||||
|
||||
**Step 1:** Fetch load balancer IP of the gateway.
|
||||
|
||||
`kubectl get gateway gateway -n default -o jsonpath='{.status.addresses[0].value}'`
|
||||
|
||||
**Step 2:** Add host entries in `/etc/hosts` with the above hostname and gateway LB IP.
|
||||
|
||||
`echo "<LB_IP> tcp1.cluster.dev" | sudo tee -a /etc/hosts`
|
||||
|
||||
**Step 3:** Fetch kubeconfig of tcp cluster.
|
||||
|
||||
`kubectl get secrets tcp-1-admin-kubeconfig -o jsonpath='{.data.admin\.conf}' | base64 -d > kubeconfig`
|
||||
|
||||
**Step 4:** Test connectivity:
|
||||
|
||||
`kubectl --kubeconfig kubeconfig cluster-info`
|
||||
|
||||
|
||||
|
||||
### Multiple Tenant Control Planes
|
||||
|
||||
We can use the same Gateway resource for multiple Tenant Control Planes by using different hostnames per tenant cluster.
|
||||
Let us extend the above example for multiple tenant control planes behind single gateway (and LB IP).
|
||||
|
||||
```yaml
|
||||
# Gateway with wildcard hostname
|
||||
@@ -160,10 +155,13 @@ metadata:
|
||||
name: gateway
|
||||
spec:
|
||||
listeners:
|
||||
- hostname: '*.cluster.dev'
|
||||
name: kube-apiserver
|
||||
- name: kube-apiserver
|
||||
port: 6443
|
||||
# ...
|
||||
# note: we changed to wildcard hostname pattern matching
|
||||
# for cluster.dev
|
||||
hostname: '*.cluster.dev'
|
||||
|
||||
# ...
|
||||
---
|
||||
# Tenant Control Plane 1
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
@@ -194,17 +192,37 @@ spec:
|
||||
# ...
|
||||
```
|
||||
|
||||
Each Tenant Control Plane will get its own TLSRoutes with the respective hostnames, all routing through the same Gateway resource.
|
||||
Each Tenant Control Plane needs to use a different hostname. For each TCP, Kamaji creates a `TLSRoutes` resource with the respective hostnames, all `TLSRoutes` routing through the same Gateway resource.
|
||||
|
||||
You can check the Gateway status in the TenantControlPlane:
|
||||
|
||||
```bash
|
||||
kubectl get tenantcontrolplane tcp-1 -o yaml
|
||||
### Konnectivity
|
||||
|
||||
If konnectivity addon is enabled, Kamaji creates a separate TLSRoute for it. But this is hardcoded with the listener name `konnectivity-server` and port `8132`. All gateways mentioned in `spec.controlPlane.gateway.parentRefs` must contain a listener with the same configuration for the given hostname. Below is example configuration:
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: gateway
|
||||
namespace: default
|
||||
spec:
|
||||
gatewayClassName: envoy-gw-class
|
||||
listeners:
|
||||
- ...
|
||||
- ...
|
||||
- name: konnectivity-server
|
||||
port: 8132
|
||||
protocol: TLS
|
||||
hostname: 'tcp1.cluster.dev'
|
||||
tls:
|
||||
mode: Passthrough
|
||||
allowedRoutes:
|
||||
kinds:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: TLSRoute
|
||||
namespaces:
|
||||
from: All
|
||||
```
|
||||
|
||||
Look for the `status.kubernetesResources.gateway` and `status.addons.konnectivity.gateway` fields.
|
||||
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Gateway API Documentation](https://gateway-api.sigs.k8s.io/)
|
||||
|
||||
@@ -48,7 +48,9 @@ var _ = Describe("Deploy a TenantControlPlane with Gateway API and Konnectivity"
|
||||
},
|
||||
GatewayParentRefs: []gatewayv1.ParentReference{
|
||||
{
|
||||
Name: "test-gateway",
|
||||
Name: "test-gateway",
|
||||
Port: pointer.To(gatewayv1.PortNumber(6443)),
|
||||
SectionName: pointer.To(gatewayv1.SectionName("cp-listener")),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -96,6 +98,35 @@ var _ = Describe("Deploy a TenantControlPlane with Gateway API and Konnectivity"
|
||||
StatusMustEqualTo(tcp, kamajiv1alpha1.VersionReady)
|
||||
})
|
||||
|
||||
It("Should create control plane TLSRoute preserving user-provided parentRef fields", func() {
|
||||
Eventually(func() error {
|
||||
route := &gatewayv1alpha2.TLSRoute{}
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{
|
||||
Name: tcp.Name,
|
||||
Namespace: tcp.Namespace,
|
||||
}, route); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(route.Spec.ParentRefs) == 0 {
|
||||
return fmt.Errorf("parentRefs is empty")
|
||||
}
|
||||
if route.Spec.ParentRefs[0].SectionName == nil {
|
||||
return fmt.Errorf("sectionName is nil")
|
||||
}
|
||||
if *route.Spec.ParentRefs[0].SectionName != gatewayv1.SectionName("cp-listener") {
|
||||
return fmt.Errorf("expected sectionName 'cp-listener', got '%s'", *route.Spec.ParentRefs[0].SectionName)
|
||||
}
|
||||
if route.Spec.ParentRefs[0].Port == nil {
|
||||
return fmt.Errorf("port is nil")
|
||||
}
|
||||
if *route.Spec.ParentRefs[0].Port != gatewayv1.PortNumber(6443) {
|
||||
return fmt.Errorf("expected port 6443, got '%d'", *route.Spec.ParentRefs[0].Port)
|
||||
}
|
||||
|
||||
return nil
|
||||
}).WithTimeout(time.Minute).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Should create Konnectivity TLSRoute with correct sectionName", func() {
|
||||
Eventually(func() error {
|
||||
route := &gatewayv1alpha2.TLSRoute{}
|
||||
|
||||
@@ -48,7 +48,9 @@ var _ = Describe("Deploy a TenantControlPlane with Gateway API", func() {
|
||||
},
|
||||
GatewayParentRefs: []gatewayv1.ParentReference{
|
||||
{
|
||||
Name: "test-gateway",
|
||||
Name: "test-gateway",
|
||||
Port: pointer.To(gatewayv1.PortNumber(6443)),
|
||||
SectionName: pointer.To(gatewayv1.SectionName("cp-listener")),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -90,7 +92,7 @@ var _ = Describe("Deploy a TenantControlPlane with Gateway API", func() {
|
||||
StatusMustEqualTo(tcp, kamajiv1alpha1.VersionReady)
|
||||
})
|
||||
|
||||
It("Should create control plane TLSRoute with correct sectionName", func() {
|
||||
It("Should create control plane TLSRoute preserving user-provided parentRef fields", func() {
|
||||
Eventually(func() error {
|
||||
route := &gatewayv1alpha2.TLSRoute{}
|
||||
// TODO: Check ownership.
|
||||
@@ -106,8 +108,14 @@ var _ = Describe("Deploy a TenantControlPlane with Gateway API", func() {
|
||||
if route.Spec.ParentRefs[0].SectionName == nil {
|
||||
return fmt.Errorf("sectionName is nil")
|
||||
}
|
||||
if *route.Spec.ParentRefs[0].SectionName != gatewayv1.SectionName("kube-apiserver") {
|
||||
return fmt.Errorf("expected sectionName 'kube-apiserver', got '%s'", *route.Spec.ParentRefs[0].SectionName)
|
||||
if *route.Spec.ParentRefs[0].SectionName != gatewayv1.SectionName("cp-listener") {
|
||||
return fmt.Errorf("expected sectionName 'cp-listener', got '%s'", *route.Spec.ParentRefs[0].SectionName)
|
||||
}
|
||||
if route.Spec.ParentRefs[0].Port == nil {
|
||||
return fmt.Errorf("port is nil")
|
||||
}
|
||||
if *route.Spec.ParentRefs[0].Port != gatewayv1.PortNumber(6443) {
|
||||
return fmt.Errorf("expected port 6443, got '%d'", *route.Spec.ParentRefs[0].Port)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+2
-2
@@ -212,7 +212,7 @@ func ScaleTenantControlPlane(tcp *kamajiv1alpha1.TenantControlPlane, replicas in
|
||||
Expect(err).To(Succeed())
|
||||
}
|
||||
|
||||
// CreateGatewayWithListeners creates a Gateway with both kube-apiserver and konnectivity-server listeners.
|
||||
// CreateGatewayWithListeners creates a Gateway with control plane and konnectivity-server listeners.
|
||||
func CreateGatewayWithListeners(gatewayName, namespace, gatewayClassName, hostname string) {
|
||||
GinkgoHelper()
|
||||
gateway := &gatewayv1.Gateway{
|
||||
@@ -224,7 +224,7 @@ func CreateGatewayWithListeners(gatewayName, namespace, gatewayClassName, hostna
|
||||
GatewayClassName: gatewayv1.ObjectName(gatewayClassName),
|
||||
Listeners: []gatewayv1.Listener{
|
||||
{
|
||||
Name: "kube-apiserver",
|
||||
Name: "cp-listener",
|
||||
Port: 6443,
|
||||
Protocol: gatewayv1.TLSProtocolType,
|
||||
Hostname: pointer.To(gatewayv1.Hostname(hostname)),
|
||||
|
||||
@@ -150,6 +150,10 @@ func (r *KubernetesGatewayResource) mutate(tcp *kamajiv1alpha1.TenantControlPlan
|
||||
tcp.Spec.ControlPlane.Gateway.AdditionalMetadata.Annotations)
|
||||
r.resource.SetAnnotations(annotations)
|
||||
|
||||
if tcp.Spec.ControlPlane.Gateway.GatewayParentRefs != nil {
|
||||
r.resource.Spec.ParentRefs = tcp.Spec.ControlPlane.Gateway.GatewayParentRefs
|
||||
}
|
||||
|
||||
serviceName := gatewayv1alpha2.ObjectName(tcp.Status.Kubernetes.Service.Name)
|
||||
servicePort := tcp.Status.Kubernetes.Service.Port
|
||||
|
||||
@@ -157,11 +161,6 @@ func (r *KubernetesGatewayResource) mutate(tcp *kamajiv1alpha1.TenantControlPlan
|
||||
return fmt.Errorf("service not ready, cannot create TLSRoute")
|
||||
}
|
||||
|
||||
if tcp.Spec.ControlPlane.Gateway.GatewayParentRefs != nil {
|
||||
// Copy parentRefs and explicitly set port and sectionName fields
|
||||
r.resource.Spec.ParentRefs = NewParentRefsSpecWithPortAndSection(tcp.Spec.ControlPlane.Gateway.GatewayParentRefs, servicePort, "kube-apiserver")
|
||||
}
|
||||
|
||||
rule := gatewayv1alpha2.TLSRouteRule{
|
||||
BackendRefs: []gatewayv1alpha2.BackendRef{
|
||||
{
|
||||
|
||||
@@ -102,30 +102,6 @@ var _ = Describe("KubernetesGatewayResource", func() {
|
||||
Expect(shouldUpdate).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should set port and sectionName in parentRefs, overriding any user-provided values", func() {
|
||||
customPort := gatewayv1.PortNumber(9999)
|
||||
customSectionName := gatewayv1.SectionName("custom")
|
||||
tcp.Spec.ControlPlane.Gateway.GatewayParentRefs[0].Port = &customPort
|
||||
tcp.Spec.ControlPlane.Gateway.GatewayParentRefs[0].SectionName = &customSectionName
|
||||
|
||||
err := resource.Define(ctx, tcp)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = resource.CreateOrUpdate(ctx, tcp)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
route := &gatewayv1alpha2.TLSRoute{}
|
||||
err = resource.Client.Get(ctx, client.ObjectKey{Name: tcp.Name, Namespace: tcp.Namespace}, route)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(route.Spec.ParentRefs).To(HaveLen(1))
|
||||
Expect(route.Spec.ParentRefs[0].Port).NotTo(BeNil())
|
||||
Expect(*route.Spec.ParentRefs[0].Port).To(Equal(tcp.Status.Kubernetes.Service.Port))
|
||||
Expect(*route.Spec.ParentRefs[0].Port).NotTo(Equal(customPort))
|
||||
Expect(route.Spec.ParentRefs[0].SectionName).NotTo(BeNil())
|
||||
Expect(*route.Spec.ParentRefs[0].SectionName).To(Equal(gatewayv1.SectionName("kube-apiserver")))
|
||||
Expect(*route.Spec.ParentRefs[0].SectionName).NotTo(Equal(customSectionName))
|
||||
})
|
||||
|
||||
It("should handle multiple parentRefs correctly", func() {
|
||||
namespace := gatewayv1.Namespace("default")
|
||||
tcp.Spec.ControlPlane.Gateway.GatewayParentRefs = []gatewayv1alpha2.ParentReference{
|
||||
@@ -149,13 +125,12 @@ var _ = Describe("KubernetesGatewayResource", func() {
|
||||
err = resource.Client.Get(ctx, client.ObjectKey{Name: tcp.Name, Namespace: tcp.Namespace}, route)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(route.Spec.ParentRefs).To(HaveLen(2))
|
||||
|
||||
for i := range route.Spec.ParentRefs {
|
||||
Expect(route.Spec.ParentRefs[i].Port).NotTo(BeNil())
|
||||
Expect(*route.Spec.ParentRefs[i].Port).To(Equal(tcp.Status.Kubernetes.Service.Port))
|
||||
Expect(route.Spec.ParentRefs[i].SectionName).NotTo(BeNil())
|
||||
Expect(*route.Spec.ParentRefs[i].SectionName).To(Equal(gatewayv1.SectionName("kube-apiserver")))
|
||||
}
|
||||
Expect(route.Spec.ParentRefs[0].Name).To(Equal(gatewayv1alpha2.ObjectName("test-gateway-1")))
|
||||
Expect(route.Spec.ParentRefs[0].Namespace).NotTo(BeNil())
|
||||
Expect(*route.Spec.ParentRefs[0].Namespace).To(Equal(namespace))
|
||||
Expect(route.Spec.ParentRefs[1].Name).To(Equal(gatewayv1alpha2.ObjectName("test-gateway-2")))
|
||||
Expect(route.Spec.ParentRefs[1].Namespace).NotTo(BeNil())
|
||||
Expect(*route.Spec.ParentRefs[1].Namespace).To(Equal(namespace))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -292,81 +267,4 @@ var _ = Describe("KubernetesGatewayResource", func() {
|
||||
Expect(listener.Port).To(Equal(gatewayv1.PortNumber(80)))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NewParentRefsSpecWithPortAndSection", func() {
|
||||
var (
|
||||
parentRefs []gatewayv1.ParentReference
|
||||
testPort int32
|
||||
testSectionName string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
namespace := gatewayv1.Namespace("default")
|
||||
namespace2 := gatewayv1.Namespace("other")
|
||||
testPort = int32(6443)
|
||||
testSectionName = "kube-apiserver"
|
||||
originalPort := gatewayv1.PortNumber(9999)
|
||||
originalSectionName := gatewayv1.SectionName("original")
|
||||
parentRefs = []gatewayv1.ParentReference{
|
||||
{
|
||||
Name: "test-gateway-1",
|
||||
Namespace: &namespace,
|
||||
Port: &originalPort,
|
||||
SectionName: &originalSectionName,
|
||||
},
|
||||
{
|
||||
Name: "test-gateway-2",
|
||||
Namespace: &namespace2,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
It("should create copy of parentRefs with port and sectionName set", func() {
|
||||
result := resources.NewParentRefsSpecWithPortAndSection(parentRefs, testPort, testSectionName)
|
||||
|
||||
Expect(result).To(HaveLen(2))
|
||||
for i := range result {
|
||||
Expect(result[i].Name).To(Equal(parentRefs[i].Name))
|
||||
Expect(result[i].Namespace).To(Equal(parentRefs[i].Namespace))
|
||||
Expect(result[i].Port).NotTo(BeNil())
|
||||
Expect(*result[i].Port).To(Equal(testPort))
|
||||
Expect(result[i].SectionName).NotTo(BeNil())
|
||||
Expect(*result[i].SectionName).To(Equal(gatewayv1.SectionName(testSectionName)))
|
||||
}
|
||||
})
|
||||
|
||||
It("should not modify original parentRefs", func() {
|
||||
// Store original values for verification
|
||||
originalFirstPort := parentRefs[0].Port
|
||||
originalFirstSectionName := parentRefs[0].SectionName
|
||||
originalSecondPort := parentRefs[1].Port
|
||||
originalSecondSectionName := parentRefs[1].SectionName
|
||||
|
||||
result := resources.NewParentRefsSpecWithPortAndSection(parentRefs, testPort, testSectionName)
|
||||
|
||||
// Original should remain unchanged
|
||||
Expect(parentRefs[0].Port).To(Equal(originalFirstPort))
|
||||
Expect(parentRefs[0].SectionName).To(Equal(originalFirstSectionName))
|
||||
Expect(parentRefs[1].Port).To(Equal(originalSecondPort))
|
||||
Expect(parentRefs[1].SectionName).To(Equal(originalSecondSectionName))
|
||||
|
||||
// Result should have new values
|
||||
Expect(result[0].Port).NotTo(BeNil())
|
||||
Expect(*result[0].Port).To(Equal(testPort))
|
||||
Expect(result[0].SectionName).NotTo(BeNil())
|
||||
Expect(*result[0].SectionName).To(Equal(gatewayv1.SectionName(testSectionName)))
|
||||
Expect(result[1].Port).NotTo(BeNil())
|
||||
Expect(*result[1].Port).To(Equal(testPort))
|
||||
Expect(result[1].SectionName).NotTo(BeNil())
|
||||
Expect(*result[1].SectionName).To(Equal(gatewayv1.SectionName(testSectionName)))
|
||||
})
|
||||
|
||||
It("should handle empty parentRefs slice", func() {
|
||||
parentRefs = []gatewayv1.ParentReference{}
|
||||
|
||||
result := resources.NewParentRefsSpecWithPortAndSection(parentRefs, testPort, testSectionName)
|
||||
|
||||
Expect(result).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -226,16 +226,3 @@ func BuildGatewayAccessPointsStatus(ctx context.Context, c client.Client, route
|
||||
|
||||
return accessPoints, nil
|
||||
}
|
||||
|
||||
// NewParentRefsSpecWithPortAndSection creates a copy of parentRefs with port and sectionName set for each reference.
|
||||
func NewParentRefsSpecWithPortAndSection(parentRefs []gatewayv1.ParentReference, port int32, sectionName string) []gatewayv1.ParentReference {
|
||||
result := make([]gatewayv1.ParentReference, len(parentRefs))
|
||||
sectionNamePtr := gatewayv1.SectionName(sectionName)
|
||||
for i, parentRef := range parentRefs {
|
||||
result[i] = *parentRef.DeepCopy()
|
||||
result[i].Port = &port
|
||||
result[i].SectionName = §ionNamePtr
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (r *KubernetesKonnectivityGatewayResource) mutate(tcp *kamajiv1alpha1.Tenan
|
||||
if tcp.Spec.ControlPlane.Gateway.GatewayParentRefs == nil {
|
||||
return fmt.Errorf("control plane gateway parentRefs are not specified")
|
||||
}
|
||||
r.resource.Spec.ParentRefs = resources.NewParentRefsSpecWithPortAndSection(tcp.Spec.ControlPlane.Gateway.GatewayParentRefs, servicePort, "konnectivity-server")
|
||||
r.resource.Spec.ParentRefs = newParentRefsSpecWithPortAndSection(tcp.Spec.ControlPlane.Gateway.GatewayParentRefs, servicePort, "konnectivity-server")
|
||||
|
||||
rule := gatewayv1alpha2.TLSRouteRule{
|
||||
BackendRefs: []gatewayv1alpha2.BackendRef{
|
||||
@@ -230,3 +230,16 @@ func (r *KubernetesKonnectivityGatewayResource) CreateOrUpdate(ctx context.Conte
|
||||
func (r *KubernetesKonnectivityGatewayResource) GetName() string {
|
||||
return "konnectivity_gateway_routes"
|
||||
}
|
||||
|
||||
// newParentRefsSpecWithPortAndSection creates a copy of parentRefs with port and sectionName set for each reference.
|
||||
func newParentRefsSpecWithPortAndSection(parentRefs []gatewayv1.ParentReference, port int32, sectionName string) []gatewayv1.ParentReference {
|
||||
result := make([]gatewayv1.ParentReference, len(parentRefs))
|
||||
sectionNamePtr := gatewayv1.SectionName(sectionName)
|
||||
for i, parentRef := range parentRefs {
|
||||
result[i] = *parentRef.DeepCopy()
|
||||
result[i].Port = &port
|
||||
result[i].SectionName = §ionNamePtr
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user