Skip to content

Wrong gateway is chosen as HTTPRoute parent #3872

@shahar-h

Description

@shahar-h

Description:
When creating 2 Gateways with the same name in 2 different namespaces, and then create for each one HTTPRoute, only one of the routes will be attached to a gateway. The reason is that Gateway is selected only by parentRef name in case namespace is omitted. The expected behavior in case namespace is omitted is to select the gateway in the local namespace.

Repro steps:
Create a GatewayClass:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
EOF

Create Gateway and HTTPRoute with www.example.com hostname in consumer1 namespace:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: consumer1
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
  namespace: consumer1
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      hostname: www.example.com
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend
  namespace: consumer1
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: consumer1
  labels:
    app: backend
    service: backend
spec:
  ports:
    - name: http
      port: 3000
      targetPort: 3000
  selector:
    app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: consumer1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: v1
  template:
    metadata:
      labels:
        app: backend
        version: v1
    spec:
      serviceAccountName: backend
      containers:
        - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e
          imagePullPolicy: IfNotPresent
          name: backend
          ports:
            - containerPort: 3000
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend
  namespace: consumer1
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "www.example.com"
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: backend
          port: 3000
          weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /
EOF

Create Gateway and HTTPRoute with www.example2.com hostname in consumer2 namespace:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: consumer2
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
  namespace: consumer2
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      hostname: www.example2.com
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend
  namespace: consumer2
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: consumer2
  labels:
    app: backend
    service: backend
spec:
  ports:
    - name: http
      port: 3000
      targetPort: 3000
  selector:
    app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: consumer2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: v1
  template:
    metadata:
      labels:
        app: backend
        version: v1
    spec:
      serviceAccountName: backend
      containers:
        - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e
          imagePullPolicy: IfNotPresent
          name: backend
          ports:
            - containerPort: 3000
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend
  namespace: consumer2
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "www.example2.com"
  rules:
    - backendRefs:
        - group: ""
          kind: Service
          name: backend
          port: 3000
          weight: 1
      matches:
        - path:
            type: PathPrefix
            value: /
EOF

consumer1/backend HTTPRoute status:

 status:
    parents:
    - conditions:
      - lastTransitionTime: "2024-07-16T20:36:36Z"
        message: Route is accepted
        observedGeneration: 1
        reason: Accepted
        status: "True"
        type: Accepted
      - lastTransitionTime: "2024-07-16T20:36:36Z"
        message: Resolved all the Object references for the Route
        observedGeneration: 1
        reason: ResolvedRefs
        status: "True"
        type: ResolvedRefs
      controllerName: gateway.envoyproxy.io/gatewayclass-controller
      parentRef:
        group: gateway.networking.k8s.io
        kind: Gateway
        name: eg

consumer2/backend HTTPRoute status:

status:
    parents:
    - conditions:
      - lastTransitionTime: "2024-07-16T20:36:36Z"
        message: No listeners included by this parent ref allowed this attachment.
        observedGeneration: 1
        reason: NotAllowedByListeners
        status: "False"
        type: Accepted
      - lastTransitionTime: "2024-07-16T20:36:36Z"
        message: Resolved all the Object references for the Route
        observedGeneration: 1
        reason: ResolvedRefs
        status: "True"
        type: ResolvedRefs
      controllerName: gateway.envoyproxy.io/gatewayclass-controller
      parentRef:
        group: gateway.networking.k8s.io
        kind: Gateway
        name: eg
  • Notice that both gateways name are eg.
  • When adding an explicit consumer2 namespace to consumer2/backend HTTPRoute parentRef the issue is resolved.
  • When looking at the code It seems like the root cause is in IsRefToGateway function:
    if parentRef.Namespace != nil && string(*parentRef.Namespace) != gateway.Namespace {
    return false
    }

    When parentRef.Namespace is nil only name is being compared.

A possible solution would be to add the route namespace in GetParentReferences to each parentRef with a missing namespace.

Environment:
Latest chart(v0.0.0-latest).

Metadata

Metadata

Assignees

Labels

kind/bugSomething isn't working

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions