From 93ce309aef795c19e50336baab26187fa398e0c0 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Sat, 26 Apr 2025 11:05:46 +0900 Subject: [PATCH 01/12] fix(translator): if routingType=Service and Host is None, push status Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index ee75ac8003..234bff2c2e 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -1401,6 +1401,11 @@ func validateDestinationSettings(destinationSettings *ir.DestinationSetting, end status.RouteReasonUnsupportedAddressType) } case resource.KindService, resource.KindServiceImport: + if endpointRoutingDisabled && isHeadlessService(destinationSettings) { + return status.NewRouteStatusError( + fmt.Errorf("service %s is headless Service, please set routingType=Endpoint", destinationSettings.Name), + status.RouteReasonUnsupportedSetting) + } if !endpointRoutingDisabled && destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { return status.NewRouteStatusError( fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported"), @@ -1411,6 +1416,17 @@ func validateDestinationSettings(destinationSettings *ir.DestinationSetting, end return nil } +// isHeadlessService reports true when any DestinationEndpoint corresponds to +// a headless Kubernetes Service (ClusterIP="None"). +func isHeadlessService(ds *ir.DestinationSetting) bool { + for _, ep := range ds.Endpoints { + if ep.Host == "None" { + return true + } + } + return false +} + func (t *Translator) processServiceImportDestinationSetting( name string, backendRef gwapiv1.BackendObjectReference, From ce548c3134aa880202db05418438fef8869e1e50 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Sat, 26 Apr 2025 11:06:24 +0900 Subject: [PATCH 02/12] test: TestValidateDestinationSettings, TestIsHeadlessService Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route_test.go | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/internal/gatewayapi/route_test.go b/internal/gatewayapi/route_test.go index 7634503971..fefb0630a9 100644 --- a/internal/gatewayapi/route_test.go +++ b/internal/gatewayapi/route_test.go @@ -9,11 +9,14 @@ import ( "fmt" "testing" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" + "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/envoyproxy/gateway/internal/ir" ) @@ -226,3 +229,97 @@ func TestGetIREndpointsFromEndpointSlices(t *testing.T) { }) } } + +func TestValidateDestinationSettings(t *testing.T) { + svcKind := gwapiv1.Kind(resource.KindService) + + tests := []struct { + name string + ds *ir.DestinationSetting + endpointRoutingDisabled bool + kind *gwapiv1.Kind + wantErr bool + wantReason gwapiv1.RouteConditionReason + }{ + { + name: "headless service rejected when endpointRoutingDisabled=true", + ds: &ir.DestinationSetting{ + Name: "headless", + Endpoints: []*ir.DestinationEndpoint{{Host: "None"}}, + }, + endpointRoutingDisabled: true, + kind: &svcKind, + wantErr: true, + wantReason: status.RouteReasonUnsupportedSetting, + }, + { + name: "normal service allowed with ClusterIP routing", + ds: &ir.DestinationSetting{ + Name: "normal", + Endpoints: []*ir.DestinationEndpoint{{Host: "10.0.0.1"}}, + }, + endpointRoutingDisabled: true, + kind: &svcKind, + wantErr: false, + }, + { + name: "mixed address type rejected when EndpointSlice routing", + ds: &ir.DestinationSetting{ + Name: "mixed", + Endpoints: []*ir.DestinationEndpoint{{Host: "10.0.0.1"}}, + AddressType: ptr.To(ir.MIXED), + }, + endpointRoutingDisabled: false, + kind: &svcKind, + wantErr: true, + wantReason: status.RouteReasonUnsupportedAddressType, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateDestinationSettings(tt.ds, tt.endpointRoutingDisabled, tt.kind) + if tt.wantErr { + require.Error(t, err) + require.Equal(t, tt.wantReason, err.Reason()) + } else { + require.NoError(t, err) + } + }) + } +} +func TestIsHeadlessService(t *testing.T) { + tests := []struct { + name string + endpoints []*ir.DestinationEndpoint + want bool + }{ + { + name: "headless Service", + endpoints: []*ir.DestinationEndpoint{ + {Host: "None"}, + }, + want: true, + }, + { + name: "non headless Service with valid ClusterIP", + endpoints: []*ir.DestinationEndpoint{ + {Host: "10.0.0.1"}, + }, + want: false, + }, + { + name: "empty slice", + endpoints: nil, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ds := &ir.DestinationSetting{Endpoints: tt.endpoints} + got := isHeadlessService(ds) + require.Equal(t, tt.want, got) + }) + } +} From 62adf31eb0cbf7265293613726ea87b7bccd1136 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Sat, 26 Apr 2025 21:40:40 +0900 Subject: [PATCH 03/12] fix: fix for linter Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/gatewayapi/route_test.go b/internal/gatewayapi/route_test.go index fefb0630a9..131e678793 100644 --- a/internal/gatewayapi/route_test.go +++ b/internal/gatewayapi/route_test.go @@ -9,8 +9,6 @@ import ( "fmt" "testing" - "github.com/envoyproxy/gateway/internal/gatewayapi/resource" - "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" @@ -18,6 +16,8 @@ import ( "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" + "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/envoyproxy/gateway/internal/ir" ) @@ -288,6 +288,7 @@ func TestValidateDestinationSettings(t *testing.T) { }) } } + func TestIsHeadlessService(t *testing.T) { tests := []struct { name string From fab6bacc044f90a850cdbd2bfbd60c2692b01212 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Sat, 26 Apr 2025 11:05:46 +0900 Subject: [PATCH 04/12] fix(translator): if routingType=Service and Host is None, push status Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 9fbbdbe880..d581689069 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -1401,6 +1401,11 @@ func validateDestinationSettings(destinationSettings *ir.DestinationSetting, end status.RouteReasonUnsupportedAddressType) } case resource.KindService, resource.KindServiceImport: + if endpointRoutingDisabled && isHeadlessService(destinationSettings) { + return status.NewRouteStatusError( + fmt.Errorf("service %s is headless Service, please set routingType=Endpoint", destinationSettings.Name), + status.RouteReasonUnsupportedSetting) + } if !endpointRoutingDisabled && destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { return status.NewRouteStatusError( fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported"), @@ -1411,6 +1416,17 @@ func validateDestinationSettings(destinationSettings *ir.DestinationSetting, end return nil } +// isHeadlessService reports true when any DestinationEndpoint corresponds to +// a headless Kubernetes Service (ClusterIP="None"). +func isHeadlessService(ds *ir.DestinationSetting) bool { + for _, ep := range ds.Endpoints { + if ep.Host == "None" { + return true + } + } + return false +} + func (t *Translator) processServiceImportDestinationSetting( name string, backendRef gwapiv1.BackendObjectReference, From 8d32a39bcb05cbdebf098513894bc8ae71814502 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Sat, 26 Apr 2025 11:06:24 +0900 Subject: [PATCH 05/12] test: TestValidateDestinationSettings, TestIsHeadlessService Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route_test.go | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/internal/gatewayapi/route_test.go b/internal/gatewayapi/route_test.go index 7634503971..fefb0630a9 100644 --- a/internal/gatewayapi/route_test.go +++ b/internal/gatewayapi/route_test.go @@ -9,11 +9,14 @@ import ( "fmt" "testing" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" + "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/envoyproxy/gateway/internal/ir" ) @@ -226,3 +229,97 @@ func TestGetIREndpointsFromEndpointSlices(t *testing.T) { }) } } + +func TestValidateDestinationSettings(t *testing.T) { + svcKind := gwapiv1.Kind(resource.KindService) + + tests := []struct { + name string + ds *ir.DestinationSetting + endpointRoutingDisabled bool + kind *gwapiv1.Kind + wantErr bool + wantReason gwapiv1.RouteConditionReason + }{ + { + name: "headless service rejected when endpointRoutingDisabled=true", + ds: &ir.DestinationSetting{ + Name: "headless", + Endpoints: []*ir.DestinationEndpoint{{Host: "None"}}, + }, + endpointRoutingDisabled: true, + kind: &svcKind, + wantErr: true, + wantReason: status.RouteReasonUnsupportedSetting, + }, + { + name: "normal service allowed with ClusterIP routing", + ds: &ir.DestinationSetting{ + Name: "normal", + Endpoints: []*ir.DestinationEndpoint{{Host: "10.0.0.1"}}, + }, + endpointRoutingDisabled: true, + kind: &svcKind, + wantErr: false, + }, + { + name: "mixed address type rejected when EndpointSlice routing", + ds: &ir.DestinationSetting{ + Name: "mixed", + Endpoints: []*ir.DestinationEndpoint{{Host: "10.0.0.1"}}, + AddressType: ptr.To(ir.MIXED), + }, + endpointRoutingDisabled: false, + kind: &svcKind, + wantErr: true, + wantReason: status.RouteReasonUnsupportedAddressType, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateDestinationSettings(tt.ds, tt.endpointRoutingDisabled, tt.kind) + if tt.wantErr { + require.Error(t, err) + require.Equal(t, tt.wantReason, err.Reason()) + } else { + require.NoError(t, err) + } + }) + } +} +func TestIsHeadlessService(t *testing.T) { + tests := []struct { + name string + endpoints []*ir.DestinationEndpoint + want bool + }{ + { + name: "headless Service", + endpoints: []*ir.DestinationEndpoint{ + {Host: "None"}, + }, + want: true, + }, + { + name: "non headless Service with valid ClusterIP", + endpoints: []*ir.DestinationEndpoint{ + {Host: "10.0.0.1"}, + }, + want: false, + }, + { + name: "empty slice", + endpoints: nil, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ds := &ir.DestinationSetting{Endpoints: tt.endpoints} + got := isHeadlessService(ds) + require.Equal(t, tt.want, got) + }) + } +} From 2a0ecf1ba8b05495b11d6444afe04c586955560d Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Sat, 26 Apr 2025 21:40:40 +0900 Subject: [PATCH 06/12] fix: fix for linter Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/gatewayapi/route_test.go b/internal/gatewayapi/route_test.go index fefb0630a9..131e678793 100644 --- a/internal/gatewayapi/route_test.go +++ b/internal/gatewayapi/route_test.go @@ -9,8 +9,6 @@ import ( "fmt" "testing" - "github.com/envoyproxy/gateway/internal/gatewayapi/resource" - "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" @@ -18,6 +16,8 @@ import ( "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" + "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/envoyproxy/gateway/internal/ir" ) @@ -288,6 +288,7 @@ func TestValidateDestinationSettings(t *testing.T) { }) } } + func TestIsHeadlessService(t *testing.T) { tests := []struct { name string From 4c069f01252fb161dd8c8a8081d154b26f2350aa Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Tue, 29 Apr 2025 16:51:55 +0900 Subject: [PATCH 07/12] test: add headless service test with routingType=Endpoint Signed-off-by: ShinyaIshitobi --- .../httproute-with-headless-service.in.yaml | 72 ++++++++++ .../httproute-with-headless-service.out.yaml | 133 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml create mode 100644 internal/gatewayapi/testdata/httproute-with-headless-service.out.yaml diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml new file mode 100644 index 0000000000..66d7a017bb --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml @@ -0,0 +1,72 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-headless + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: headless-service + port: 80 +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: headless-service + spec: + clusterIP: None + selector: + app: myapp + ports: + - port: 80 + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: headless-service-slice + namespace: default + labels: + kubernetes.io/service-name: headless-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 80 + endpoints: + - addresses: + - "192.168.1.1" + conditions: + ready: true + - addresses: + - "192.168.1.2" + conditions: + ready: true +envoyProxyForGatewayClass: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + name: default + namespace: default + spec: + routingType: Endpoint \ No newline at end of file diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service.out.yaml new file mode 100644 index 0000000000..884bc76439 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-headless-service.out.yaml @@ -0,0 +1,133 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-headless + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: headless-service + port: 80 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + name: default + namespace: default + spec: + routingType: Endpoint + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: null + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-headless + namespace: default + name: httproute/default/httproute-headless/rule/0/match/-1/* + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 From a8132fa2e5dff923a247f24732fa084fba8ebfa6 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Tue, 29 Apr 2025 17:01:36 +0900 Subject: [PATCH 08/12] test: add headless service test with routingType=Service Signed-off-by: ShinyaIshitobi --- ...h-headless-service-service-routing.in.yaml | 72 ++++++++++ ...-headless-service-service-routing.out.yaml | 133 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml create mode 100644 internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml new file mode 100644 index 0000000000..2eff22661f --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml @@ -0,0 +1,72 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-headless + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: headless-service + port: 80 +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: default + name: headless-service + spec: + clusterIP: None + selector: + app: myapp + ports: + - port: 80 + targetPort: 8080 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: headless-service-slice + namespace: default + labels: + kubernetes.io/service-name: headless-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 80 + endpoints: + - addresses: + - "192.168.1.1" + conditions: + ready: true + - addresses: + - "192.168.1.2" + conditions: + ready: true +envoyProxyForGatewayClass: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + name: default + namespace: default + spec: + routingType: Service diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml new file mode 100644 index 0000000000..e5f8037d7f --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml @@ -0,0 +1,133 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: "2023-02-22T14:15:22Z" + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: "2023-02-22T14:15:22Z" + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2023-02-22T14:15:22Z" + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-headless + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: headless-service + port: 80 + status: + parents: + - conditions: + - lastTransitionTime: "2023-02-22T14:15:22Z" + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: "2023-02-22T14:15:22Z" + message: "Failed to process route rule 0 backendRef 0: service httproute/default/httproute-headless/rule/0/backend/0 is headless Service, please set routingType=Endpoint." + reason: UnsupportedValue + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + name: default + namespace: default + spec: + routingType: Service + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: null + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-headless + namespace: default + name: httproute/default/httproute-headless/rule/0/match/-1/* + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 From 3730c63531fdf172dcf6c731b8da12e0e8ddb19c Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Tue, 29 Apr 2025 17:07:05 +0900 Subject: [PATCH 09/12] test: rename file name and remove endpointSlices Signed-off-by: ShinyaIshitobi --- ...headless-service-endpoint-routing.in.yaml} | 27 +------------------ ...eadless-service-endpoint-routing.out.yaml} | 0 ...h-headless-service-service-routing.in.yaml | 25 ----------------- 3 files changed, 1 insertion(+), 51 deletions(-) rename internal/gatewayapi/testdata/{httproute-with-headless-service.in.yaml => httproute-with-headless-service-endpoint-routing.in.yaml} (64%) rename internal/gatewayapi/testdata/{httproute-with-headless-service.out.yaml => httproute-with-headless-service-endpoint-routing.out.yaml} (100%) diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.in.yaml similarity index 64% rename from internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml rename to internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.in.yaml index 66d7a017bb..f8894d999c 100644 --- a/internal/gatewayapi/testdata/httproute-with-headless-service.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.in.yaml @@ -35,33 +35,8 @@ services: name: headless-service spec: clusterIP: None - selector: - app: myapp ports: - port: 80 - targetPort: 8080 -endpointSlices: -- apiVersion: discovery.k8s.io/v1 - kind: EndpointSlice - metadata: - name: headless-service-slice - namespace: default - labels: - kubernetes.io/service-name: headless-service - addressType: IPv4 - ports: - - name: http - protocol: TCP - port: 80 - endpoints: - - addresses: - - "192.168.1.1" - conditions: - ready: true - - addresses: - - "192.168.1.2" - conditions: - ready: true envoyProxyForGatewayClass: apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy @@ -69,4 +44,4 @@ envoyProxyForGatewayClass: name: default namespace: default spec: - routingType: Endpoint \ No newline at end of file + routingType: Endpoint diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.out.yaml similarity index 100% rename from internal/gatewayapi/testdata/httproute-with-headless-service.out.yaml rename to internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.out.yaml diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml index 2eff22661f..24f19097a9 100644 --- a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.in.yaml @@ -35,33 +35,8 @@ services: name: headless-service spec: clusterIP: None - selector: - app: myapp ports: - port: 80 - targetPort: 8080 -endpointSlices: -- apiVersion: discovery.k8s.io/v1 - kind: EndpointSlice - metadata: - name: headless-service-slice - namespace: default - labels: - kubernetes.io/service-name: headless-service - addressType: IPv4 - ports: - - name: http - protocol: TCP - port: 80 - endpoints: - - addresses: - - "192.168.1.1" - conditions: - ready: true - - addresses: - - "192.168.1.2" - conditions: - ready: true envoyProxyForGatewayClass: apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy From 5da16866aad4babce650ae4f76607f3843495282 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Tue, 29 Apr 2025 17:12:43 +0900 Subject: [PATCH 10/12] test: lastTransitionTime should be null Signed-off-by: ShinyaIshitobi --- ...oute-with-headless-service-service-routing.out.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml index e5f8037d7f..dfb4dcedc7 100644 --- a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml @@ -18,17 +18,17 @@ gateways: listeners: - attachedRoutes: 1 conditions: - - lastTransitionTime: "2023-02-22T14:15:22Z" + - lastTransitionTime: null message: Sending translated listener configuration to the data plane reason: Programmed status: "True" type: Programmed - - lastTransitionTime: "2023-02-22T14:15:22Z" + - lastTransitionTime: null message: Listener has been successfully translated reason: Accepted status: "True" type: Accepted - - lastTransitionTime: "2023-02-22T14:15:22Z" + - lastTransitionTime: null message: Listener references have been resolved reason: ResolvedRefs status: "True" @@ -57,12 +57,12 @@ httpRoutes: status: parents: - conditions: - - lastTransitionTime: "2023-02-22T14:15:22Z" + - lastTransitionTime: null message: Route is accepted reason: Accepted status: "True" type: Accepted - - lastTransitionTime: "2023-02-22T14:15:22Z" + - lastTransitionTime: null message: "Failed to process route rule 0 backendRef 0: service httproute/default/httproute-headless/rule/0/backend/0 is headless Service, please set routingType=Endpoint." reason: UnsupportedValue status: "False" From 0c308d7884dafe38bc9c713235638e0457dd210e Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Tue, 29 Apr 2025 17:50:25 +0900 Subject: [PATCH 11/12] chore: make gen-check Signed-off-by: ShinyaIshitobi --- ...route-with-headless-service-endpoint-routing.out.yaml | 6 ++++-- ...proute-with-headless-service-service-routing.out.yaml | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.out.yaml index 884bc76439..062854db6d 100644 --- a/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-endpoint-routing.out.yaml @@ -78,10 +78,13 @@ infraIR: apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: + creationTimestamp: null name: default namespace: default spec: + logging: {} routingType: Endpoint + status: {} listeners: - address: null name: envoy-gateway/gateway-1/http @@ -116,8 +119,7 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - destination: null - directResponse: + - directResponse: statusCode: 500 hostname: '*' isHTTP2: false diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml index dfb4dcedc7..aaa3054986 100644 --- a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml @@ -63,7 +63,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: "Failed to process route rule 0 backendRef 0: service httproute/default/httproute-headless/rule/0/backend/0 is headless Service, please set routingType=Endpoint." + message: 'Failed to process route rule 0 backendRef 0: service httproute/default/httproute-headless/rule/0/backend/0 + is headless Service, please set routingType=Endpoint.' reason: UnsupportedValue status: "False" type: ResolvedRefs @@ -78,10 +79,13 @@ infraIR: apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyProxy metadata: + creationTimestamp: null name: default namespace: default spec: + logging: {} routingType: Service + status: {} listeners: - address: null name: envoy-gateway/gateway-1/http @@ -116,8 +120,7 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - destination: null - directResponse: + - directResponse: statusCode: 500 hostname: '*' isHTTP2: false From bebc4c5547272c722be09c80e77b1d432f811ec0 Mon Sep 17 00:00:00 2001 From: ShinyaIshitobi Date: Wed, 30 Apr 2025 01:18:05 +0900 Subject: [PATCH 12/12] fix: add a Signed-off-by: ShinyaIshitobi --- internal/gatewayapi/route.go | 2 +- .../httproute-with-headless-service-service-routing.out.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index d581689069..539edbf519 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -1403,7 +1403,7 @@ func validateDestinationSettings(destinationSettings *ir.DestinationSetting, end case resource.KindService, resource.KindServiceImport: if endpointRoutingDisabled && isHeadlessService(destinationSettings) { return status.NewRouteStatusError( - fmt.Errorf("service %s is headless Service, please set routingType=Endpoint", destinationSettings.Name), + fmt.Errorf("service %s is a headless Service, please set routingType=Endpoint", destinationSettings.Name), status.RouteReasonUnsupportedSetting) } if !endpointRoutingDisabled && destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { diff --git a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml index aaa3054986..0643b11e03 100644 --- a/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-headless-service-service-routing.out.yaml @@ -64,7 +64,7 @@ httpRoutes: type: Accepted - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: service httproute/default/httproute-headless/rule/0/backend/0 - is headless Service, please set routingType=Endpoint.' + is a headless Service, please set routingType=Endpoint.' reason: UnsupportedValue status: "False" type: ResolvedRefs