diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index b854d424a..7d1ca1346 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -93,8 +93,77 @@ jobs: gateway-conformance-test: runs-on: ubuntu-22.04 needs: [build] + env: + GATEWAY_API_TEST_NAMESPACE: gateway-conformance-infra + HIGRESS_TEST_IMAGE_TAG: ${{ github.event.pull_request.head.sha || github.sha }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + ref: ${{ env.HIGRESS_TEST_IMAGE_TAG }} + + - name: Disable containerd image store + run: | + sudo bash -c 'cat > /etc/docker/daemon.json << EOF + { + "features": { + "containerd-snapshotter": false + } + } + EOF' + sudo systemctl restart docker + docker info -f '{{ .DriverStatus }}' + + - name: Free Up GitHub Actions Ubuntu Runner Disk Space 🔧 + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: true + swap-storage: true + + - name: "Setup Go" + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Setup Golang Caches + uses: actions/cache@v4 + with: + path: |- + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ github.run_id }} + restore-keys: ${{ runner.os }}-go + + - name: Run Higress Gateway API Tests + run: |- + GOPROXY="https://proxy.golang.org,direct" make gateway-conformance-test \ + TAG="${HIGRESS_TEST_IMAGE_TAG}" \ + HIGRESS_CONFORMANCE_VERSION="${HIGRESS_TEST_IMAGE_TAG}" + + - name: Collect Gateway API Test Diagnostics + if: always() + run: |- + mkdir -p out/gateway-api-conformance/diagnostics + kubectl get gatewayclass,gateway,httproute,referencegrant -A -o yaml > out/gateway-api-conformance/diagnostics/resources.yaml 2>&1 || true + kubectl get events -A --sort-by=.lastTimestamp > out/gateway-api-conformance/diagnostics/events.txt 2>&1 || true + kubectl logs -n "${GATEWAY_API_TEST_NAMESPACE}" deployment/higress-controller --all-containers > out/gateway-api-conformance/diagnostics/controller.log 2>&1 || true + kubectl logs -n "${GATEWAY_API_TEST_NAMESPACE}" deployment/higress-gateway --all-containers > out/gateway-api-conformance/diagnostics/gateway.log 2>&1 || true + tools/bin/kind-gateway-api export logs out/gateway-api-conformance/diagnostics/kind --name higress || true + + - name: Upload Gateway API Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: gateway-conformance-test + path: out/gateway-api-conformance/ + if-no-files-found: warn + + - name: Clean Gateway API Test Environment + if: always() + run: make gateway-conformance-test-clean higress-conformance-test: runs-on: ubuntu-22.04 diff --git a/Makefile.core.mk b/Makefile.core.mk index d52788fcd..759150850 100644 --- a/Makefile.core.mk +++ b/Makefile.core.mk @@ -21,6 +21,21 @@ GO ?= go export GOPROXY ?= https://proxy.golang.org,direct +GATEWAY_API_VERSION ?= v1.4.0 +GATEWAY_CONFORMANCE_PROFILE ?= GATEWAY-HTTP +GATEWAY_CONFORMANCE_SUPPORTED_FEATURES ?= Gateway,HTTPRoute,ReferenceGrant +GATEWAY_CONFORMANCE_REPORT ?= out/gateway-api-conformance/report.yaml +GATEWAY_CONFORMANCE_CONTACT ?= https://github.com/alibaba/higress/issues +GATEWAY_CONFORMANCE_RUN_TEST ?= +GATEWAY_CONFORMANCE_ALLOW_CRDS_MISMATCH ?= false +GATEWAY_API_TEST_NAMESPACE ?= gateway-conformance-infra +GATEWAY_API_GATEWAY_SERVICE_TYPE ?= ClusterIP +GATEWAY_API_DIAL_LOCALHOST ?= true +GATEWAY_API_LOCAL_HTTP_PORT ?= 80 +GATEWAY_API_LOCAL_HTTPS_PORT ?= 443 +GATEWAY_API_KIND_NODE_TAG ?= v1.34.0@sha256:7416a61b42b1662ca6ca89f02028ac133a309a2a30ba309614e8ec94d976dc5a +HIGRESS_CONFORMANCE_VERSION ?= $(shell git rev-parse HEAD) + TARGET_ARCH ?= amd64 VALID_ARCHS := amd64 arm64 @@ -210,6 +225,10 @@ ISTIO_LATEST_IMAGE_TAG ?= de2c9628294f51b13c4a70b3a862b4372890797a install-dev: pre-install helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' + +install-dev-gateway-api: pre-install + helm install higress helm/core -n $(GATEWAY_API_TEST_NAMESPACE) --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'gateway.service.type=$(GATEWAY_API_GATEWAY_SERVICE_TYPE)' + install-dev-wasmplugin: build-wasmplugins pre-install helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' --set 'global.volumeWasmPlugins=true' --set 'global.onlyPushRouteCluster=false' @@ -262,9 +281,71 @@ clean: clean-higress clean-gateway clean-istio clean-env clean-tool include tools/tools.mk include tools/lint.mk -# gateway-conformance-test runs gateway api conformance tests. +# install-gateway-api-crds installs the Gateway API CRDs used by the conformance suite. +.PHONY: install-gateway-api-crds +install-gateway-api-crds: + kubectl apply --server-side=true -f https://github.com/kubernetes-sigs/gateway-api/releases/download/$(GATEWAY_API_VERSION)/standard-install.yaml + kubectl wait --for=condition=Established crd/gatewayclasses.gateway.networking.k8s.io --timeout=120s + kubectl wait --for=condition=Established crd/gateways.gateway.networking.k8s.io --timeout=120s + kubectl wait --for=condition=Established crd/httproutes.gateway.networking.k8s.io --timeout=120s + kubectl wait --for=condition=Established crd/referencegrants.gateway.networking.k8s.io --timeout=120s + +# create-gateway-api-cluster creates the Kubernetes version used by Gateway API v1.4.0 tests. +.PHONY: create-gateway-api-cluster +create-gateway-api-cluster: $(tools/kind-gateway-api) + KIND=$(tools/kind-gateway-api) KIND_NODE_TAG=$(GATEWAY_API_KIND_NODE_TAG) tools/hack/create-cluster.sh + +# delete-gateway-api-cluster deletes the Gateway API test cluster. +.PHONY: delete-gateway-api-cluster +delete-gateway-api-cluster: $(tools/kind-gateway-api) + $(tools/kind-gateway-api) delete cluster --name higress + +# kube-load-gateway-api-images loads only the images required by the Gateway API tests. +.PHONY: kube-load-gateway-api-images +kube-load-gateway-api-images: $(tools/kind-gateway-api) + KIND=$(tools/kind-gateway-api) tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/higress $(TAG) + tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/pilot $(ISTIO_LATEST_IMAGE_TAG) + tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway $(ENVOY_LATEST_IMAGE_TAG) + KIND=$(tools/kind-gateway-api) tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/pilot $(ISTIO_LATEST_IMAGE_TAG) + KIND=$(tools/kind-gateway-api) tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway $(ENVOY_LATEST_IMAGE_TAG) + +# gateway-conformance-test-prepare prepares a kind cluster for Gateway API tests. +.PHONY: gateway-conformance-test-prepare +gateway-conformance-test-prepare: delete-gateway-api-cluster create-gateway-api-cluster install-gateway-api-crds docker-build kube-load-gateway-api-images install-dev-gateway-api + kubectl wait --timeout=10m -n $(GATEWAY_API_TEST_NAMESPACE) deployment/higress-controller --for=condition=Available + kubectl wait --timeout=10m -n $(GATEWAY_API_TEST_NAMESPACE) deployment/higress-gateway --for=condition=Available + kubectl wait --timeout=10m gatewayclass/higress --for=condition=Accepted + +# run-gateway-conformance-test runs the upstream Gateway API Conformance Suite. +.PHONY: run-gateway-conformance-test +run-gateway-conformance-test: + mkdir -p $(dir $(GATEWAY_CONFORMANCE_REPORT)) + HIGRESS_GATEWAY_API_TEST_DIAL_LOCALHOST=$(GATEWAY_API_DIAL_LOCALHOST) \ + HIGRESS_GATEWAY_API_TEST_LOCAL_HTTP_PORT=$(GATEWAY_API_LOCAL_HTTP_PORT) \ + HIGRESS_GATEWAY_API_TEST_LOCAL_HTTPS_PORT=$(GATEWAY_API_LOCAL_HTTPS_PORT) go test -v ./test/gateway \ + -run '^TestGatewayAPIConformance$$' \ + -args \ + --gateway-class=higress \ + --supported-features=$(GATEWAY_CONFORMANCE_SUPPORTED_FEATURES) \ + --conformance-profiles=$(GATEWAY_CONFORMANCE_PROFILE) \ + --organization=alibaba \ + --project=higress \ + --url=https://github.com/alibaba/higress \ + --version=$(HIGRESS_CONFORMANCE_VERSION) \ + --contact=$(GATEWAY_CONFORMANCE_CONTACT) \ + --mode=default \ + --cleanup-base-resources=false \ + --allow-crds-mismatch=$(GATEWAY_CONFORMANCE_ALLOW_CRDS_MISMATCH) \ + $(if $(GATEWAY_CONFORMANCE_RUN_TEST),--run-test=$(GATEWAY_CONFORMANCE_RUN_TEST),) \ + --report-output=$(abspath $(GATEWAY_CONFORMANCE_REPORT)) + +# gateway-conformance-test runs Gateway API tests as a standard Higress integration test. .PHONY: gateway-conformance-test -gateway-conformance-test: +gateway-conformance-test: gateway-conformance-test-prepare run-gateway-conformance-test + +# gateway-conformance-test-clean deletes the Gateway API test cluster. +.PHONY: gateway-conformance-test-clean +gateway-conformance-test-clean: delete-gateway-api-cluster # higress-conformance-test-prepare prepares the environment for higress conformance tests. .PHONY: higress-conformance-test-prepare diff --git a/go.mod b/go.mod index f372a164a..1f5db15f7 100644 --- a/go.mod +++ b/go.mod @@ -128,6 +128,7 @@ require ( github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.2 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect diff --git a/go.sum b/go.sum index 5221a0e31..191e89fb9 100644 --- a/go.sum +++ b/go.sum @@ -5345,7 +5345,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2/go. google.golang.org/genproto/googleapis/api v0.0.0-20250227231956-55c901821b1e/go.mod h1:Xsh8gBVxGCcbV8ZeTB9wI5XPyZ5RvC6V3CTeeplHbiA= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= -google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= @@ -5475,8 +5474,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= @@ -5564,7 +5561,6 @@ google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7 google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= diff --git a/pkg/ingress/kube/gateway/istio/controller_test.go b/pkg/ingress/kube/gateway/istio/controller_test.go index f8641cc53..fb58355cf 100644 --- a/pkg/ingress/kube/gateway/istio/controller_test.go +++ b/pkg/ingress/kube/gateway/istio/controller_test.go @@ -21,6 +21,7 @@ import ( "strings" "testing" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -31,6 +32,7 @@ import ( networking "istio.io/api/networking/v1alpha3" "istio.io/istio/pilot/pkg/networking/core" "istio.io/istio/pilot/pkg/serviceregistry/kube/controller" + "istio.io/istio/pilot/pkg/status" "istio.io/istio/pkg/config" "istio.io/istio/pkg/config/constants" "istio.io/istio/pkg/config/schema/gvk" @@ -192,6 +194,90 @@ func TestListGatewayResourceType(t *testing.T) { } } +func TestListGatewayResourceTypeWithAlternateGatewayClassName(t *testing.T) { + alternateGateway := gatewaySpec.DeepCopy() + alternateGateway.GatewayClassName = "alternate" + controller := setupController(t, + &k8sbeta.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{Name: "alternate"}, + Spec: *gatewayClassSpec, + }, + &k8sbeta.Gateway{ + ObjectMeta: metav1.ObjectMeta{Name: "alternate-gw", Namespace: "ns1"}, + Spec: *alternateGateway, + }) + + cfg := controller.List(gvk.Gateway, "ns1") + assert.Equal(t, len(cfg), 1) + assert.Equal(t, cfg[0].Name, "alternate-gw-"+constants.KubernetesGatewayName+"-default") +} + +func TestHTTPRouteBackendServiceLifecycle(t *testing.T) { + backendPort := k8s.PortNumber(80) + route := httpRouteSpec.DeepCopy() + route.Rules = []k8s.HTTPRouteRule{{ + BackendRefs: []k8s.HTTPBackendRef{{ + BackendRef: k8s.BackendRef{ + BackendObjectReference: k8s.BackendObjectReference{ + Name: "backend", + Port: &backendPort, + }, + }, + }}, + }} + controller := setupController(t, + &k8sbeta.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{Name: "higress"}, + Spec: *gatewayClassSpec, + }, + &k8sbeta.Gateway{ + ObjectMeta: metav1.ObjectMeta{Name: "gwspec", Namespace: "ns1"}, + Spec: *gatewaySpec, + }, + &k8sbeta.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{Name: "http-route", Namespace: "ns1"}, + Spec: *route, + }, + ) + + statusQueue := &TestStatusQueue{state: map[status.Resource]any{}} + setAndWait(t, controller, statusQueue) + resolvedRefs := func() string { + for _, rawStatus := range statusQueue.Statuses() { + routeStatus, ok := rawStatus.(*k8s.HTTPRouteStatus) + if !ok { + continue + } + for _, parent := range routeStatus.Parents { + for _, condition := range parent.Conditions { + if condition.Type == string(k8s.RouteConditionResolvedRefs) { + return string(condition.Status) + "/" + condition.Reason + } + } + } + } + return "" + } + assert.EventuallyEqual(t, resolvedRefs, "False/BackendNotFound") + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{Name: "backend", Namespace: "ns1"}, + Spec: corev1.ServiceSpec{Ports: []corev1.ServicePort{{ + Name: "http", + Port: int32(backendPort), + }}}, + } + if _, err := controller.client.Kube().CoreV1().Services("ns1").Create(t.Context(), service, metav1.CreateOptions{}); err != nil { + t.Fatal(err) + } + assert.EventuallyEqual(t, resolvedRefs, "True/ResolvedRefs") + + if err := controller.client.Kube().CoreV1().Services("ns1").Delete(t.Context(), service.Name, metav1.DeleteOptions{}); err != nil { + t.Fatal(err) + } + assert.EventuallyEqual(t, resolvedRefs, "False/BackendNotFound") +} + func TestListGatewayResourceTypeWithCustomGatewayClass(t *testing.T) { if runInGatewayClassSubprocess(t) { return diff --git a/pkg/ingress/kube/gateway/istio/conversion.go b/pkg/ingress/kube/gateway/istio/conversion.go index c906aef69..9637946e3 100644 --- a/pkg/ingress/kube/gateway/istio/conversion.go +++ b/pkg/ingress/kube/gateway/istio/conversion.go @@ -1064,12 +1064,12 @@ func buildDestination(ctx RouteContext, to k8s.BackendRef, ns string, return nil, nil, &ConfigError{Reason: InvalidDestination, Message: "service name invalid; the name of the Service must be used, not the hostname."} } hostname = fmt.Sprintf("%s.%s.svc.%s", to.Name, namespace, ctx.DomainSuffix) - // Start - Updated by Higress - //key := namespace + "/" + string(to.Name) - //svc := ptr.Flatten(krt.FetchOne(ctx.Krt, ctx.Services, krt.FilterKey(key))) - svc := ctx.LookupHostname(hostname, namespace, "Service") - // End - Updated by Higress - if svc == nil { + // Keep Higress service resolution as the primary lookup so its service naming and + // plugin binding semantics remain unchanged. Always fetch from the informer to + // retain the dependency needed to recompute the Route when the Service is deleted. + key := namespace + "/" + string(to.Name) + kubeSvc := ptr.Flatten(krt.FetchOne(ctx.Krt, ctx.Services, krt.FilterKey(key))) + if ctx.LookupHostname(hostname, namespace, "Service") == nil && kubeSvc == nil { invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)} } case config.GroupVersionKind{Group: gvk.ServiceEntry.Group, Kind: "Hostname"}: @@ -1592,6 +1592,21 @@ func (r routeParentReference) IsMesh() bool { return r.InternalName == "mesh" } +func (r routeParentReference) hostnameIntersection(rawRouteHost string) (string, bool) { + routeHost := host.Name(rawRouteHost) + listenerHost := host.Name(r.Hostname) + if len(listenerHost) == 0 || listenerHost == "*" { + return rawRouteHost, true + } + if routeHost.SubsetOf(listenerHost) { + return rawRouteHost, true + } + if listenerHost.SubsetOf(routeHost) { + return r.Hostname, true + } + return "", false +} + func (r routeParentReference) hostnameAllowedByIsolation(rawRouteHost string) bool { routeHost := host.Name(rawRouteHost) ourListener := host.Name(r.Hostname) diff --git a/pkg/ingress/kube/gateway/istio/conversion_test.go b/pkg/ingress/kube/gateway/istio/conversion_test.go index 2f40eef5f..64d38b59d 100644 --- a/pkg/ingress/kube/gateway/istio/conversion_test.go +++ b/pkg/ingress/kube/gateway/istio/conversion_test.go @@ -1522,6 +1522,36 @@ func TestGatewayReferenceAllowedParentHostnameParsing(t *testing.T) { } } +func TestRouteParentReferenceHostnameIntersection(t *testing.T) { + tests := []struct { + name string + listenerHost string + routeHost string + wantHost string + wantMatch bool + }{ + {name: "empty listener", listenerHost: "", routeHost: "example.com", wantHost: "example.com", wantMatch: true}, + {name: "empty route", listenerHost: "example.com", routeHost: "*", wantHost: "example.com", wantMatch: true}, + {name: "exact match", listenerHost: "example.com", routeHost: "example.com", wantHost: "example.com", wantMatch: true}, + {name: "exact listener and wildcard route", listenerHost: "foo.example.com", routeHost: "*.example.com", wantHost: "foo.example.com", wantMatch: true}, + {name: "wildcard listener and exact route", listenerHost: "*.example.com", routeHost: "foo.example.com", wantHost: "foo.example.com", wantMatch: true}, + {name: "narrower route wildcard", listenerHost: "*.example.com", routeHost: "*.foo.example.com", wantHost: "*.foo.example.com", wantMatch: true}, + {name: "narrower listener wildcard", listenerHost: "*.foo.example.com", routeHost: "*.example.com", wantHost: "*.foo.example.com", wantMatch: true}, + {name: "disjoint exact hosts", listenerHost: "foo.example.com", routeHost: "bar.example.com", wantMatch: false}, + {name: "disjoint wildcard hosts", listenerHost: "*.example.com", routeHost: "*.example.net", wantMatch: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parent := routeParentReference{Hostname: tt.listenerHost} + gotHost, gotMatch := parent.hostnameIntersection(tt.routeHost) + if gotHost != tt.wantHost || gotMatch != tt.wantMatch { + t.Fatalf("hostnameIntersection() = (%q, %v), want (%q, %v)", gotHost, gotMatch, tt.wantHost, tt.wantMatch) + } + }) + } +} + func TestReferencePolicy(t *testing.T) { validator := crdvalidation.NewIstioValidator(t) type res struct { diff --git a/pkg/ingress/kube/gateway/istio/gatewayclass_collection.go b/pkg/ingress/kube/gateway/istio/gatewayclass_collection.go index 5504f6bf9..c370bcf7d 100644 --- a/pkg/ingress/kube/gateway/istio/gatewayclass_collection.go +++ b/pkg/ingress/kube/gateway/istio/gatewayclass_collection.go @@ -38,8 +38,7 @@ func GatewayClassesCollection( krt.Collection[GatewayClass], ) { return krt.NewStatusCollection(gatewayClasses, func(ctx krt.HandlerContext, obj *gateway.GatewayClass) (*gateway.GatewayClassStatus, *GatewayClass) { - if gatewayv1.ObjectName(obj.Name) != gatewayv1.ObjectName(gatewayClassName) || - obj.Spec.ControllerName != managedGatewayController { + if obj.Spec.ControllerName != managedGatewayController { return nil, nil } if _, known := classInfos[obj.Spec.ControllerName]; !known { diff --git a/pkg/ingress/kube/gateway/istio/route_collections.go b/pkg/ingress/kube/gateway/istio/route_collections.go index 16e40e1a9..197de3ec5 100644 --- a/pkg/ingress/kube/gateway/istio/route_collections.go +++ b/pkg/ingress/kube/gateway/istio/route_collections.go @@ -148,7 +148,12 @@ func HTTPRouteCollection( } // Create one VS per hostname with a single hostname. // This ensures we can treat each hostname independently, as the spec requires - for _, h := range vsHosts { + processedHosts := sets.New[string]() + for _, routeHost := range vsHosts { + h, ok := parent.hostnameIntersection(routeHost) + if !ok || processedHosts.InsertContains(h) { + continue + } if !parent.hostnameAllowedByIsolation(h) { // TODO: standardize a status message for this upstream and report continue diff --git a/pkg/ingress/kube/gateway/istio/testdata/backend-tls-policy.yaml.golden b/pkg/ingress/kube/gateway/istio/testdata/backend-tls-policy.yaml.golden index 1d3cbd508..523f36321 100644 --- a/pkg/ingress/kube/gateway/istio/testdata/backend-tls-policy.yaml.golden +++ b/pkg/ingress/kube/gateway/istio/testdata/backend-tls-policy.yaml.golden @@ -23,13 +23,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/http.higress-system internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~*.domain.example namespace: higress-system spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-default hosts: - - '*' + - '*.domain.example' http: - name: http route: diff --git a/pkg/ingress/kube/gateway/istio/testdata/delegated.yaml.golden b/pkg/ingress/kube/gateway/istio/testdata/delegated.yaml.golden index d2cd9a069..0bfadaab8 100644 --- a/pkg/ingress/kube/gateway/istio/testdata/delegated.yaml.golden +++ b/pkg/ingress/kube/gateway/istio/testdata/delegated.yaml.golden @@ -42,13 +42,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/http.apple internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-apple~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-apple~apple.example namespace: apple spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-apple hosts: - - '*' + - apple.example http: - name: apple/http route: @@ -63,13 +63,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/http.banana internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-banana~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-banana~banana.example namespace: banana spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-banana hosts: - - '*' + - banana.example http: - name: banana/http route: diff --git a/pkg/ingress/kube/gateway/istio/testdata/http.yaml.golden b/pkg/ingress/kube/gateway/istio/testdata/http.yaml.golden index 5f4a82332..bfaf3d2fd 100644 --- a/pkg/ingress/kube/gateway/istio/testdata/http.yaml.golden +++ b/pkg/ingress/kube/gateway/istio/testdata/http.yaml.golden @@ -23,13 +23,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/mirror.default,HTTPRoute/redirect.default,HTTPRoute/rewrite.default internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~*.domain.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-default hosts: - - '*' + - '*.domain.example' http: - match: - uri: diff --git a/pkg/ingress/kube/gateway/istio/testdata/invalid.yaml.golden b/pkg/ingress/kube/gateway/istio/testdata/invalid.yaml.golden index 7bb399215..5fb2635b1 100644 --- a/pkg/ingress/kube/gateway/istio/testdata/invalid.yaml.golden +++ b/pkg/ingress/kube/gateway/istio/testdata/invalid.yaml.golden @@ -142,13 +142,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/invalid-mirror.default internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~*.domain.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-default hosts: - - '*' + - '*.domain.example' http: - name: default/invalid-mirror route: diff --git a/pkg/ingress/kube/gateway/istio/testdata/route-binding.yaml.golden b/pkg/ingress/kube/gateway/istio/testdata/route-binding.yaml.golden index cf4cdf928..bb7f47867 100644 --- a/pkg/ingress/kube/gateway/istio/testdata/route-binding.yaml.golden +++ b/pkg/ingress/kube/gateway/istio/testdata/route-binding.yaml.golden @@ -305,13 +305,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/bind-all.default internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-default~*.domain.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-default hosts: - - '*' + - '*.domain.example' http: - name: default/bind-all route: @@ -326,13 +326,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/bind-all.default,HTTPRoute/same-namespace-valid.istio-system internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-foobar~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-foobar~*.foobar.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-foobar hosts: - - '*' + - '*.foobar.example' http: - name: default/bind-all route: @@ -374,13 +374,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/bind-all.default,HTTPRoute/bind-cross-namespace.group-namespace1 internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-dne-no~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-dne-no~*.slctr-expr-dne-no.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-slctr-expr-dne-no hosts: - - '*' + - '*.slctr-expr-dne-no.example' http: - name: default/bind-all route: @@ -401,13 +401,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/bind-all.default,HTTPRoute/bind-cross-namespace.group-namespace1 internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-dne-yes~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-dne-yes~*.slctr-expr-dne-yes.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-slctr-expr-dne-yes hosts: - - '*' + - '*.slctr-expr-dne-yes.example' http: - name: default/bind-all route: @@ -428,13 +428,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/bind-all.default,HTTPRoute/bind-cross-namespace.group-namespace1 internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-notin-no~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-notin-no~*.slctr-expr-notin-no.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-slctr-expr-notin-no hosts: - - '*' + - '*.slctr-expr-notin-no.example' http: - name: default/bind-all route: @@ -455,13 +455,13 @@ metadata: annotations: internal.istio.io/parents: HTTPRoute/bind-all.default,HTTPRoute/bind-cross-namespace.group-namespace1 internal.istio.io/route-semantics: gateway - name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-notin-yes~* + name: higress-system~gateway-istio-autogenerated-k8s-gateway-slctr-expr-notin-yes~*.slctr-expr-notin-yes.example namespace: default spec: gateways: - higress-system/gateway-istio-autogenerated-k8s-gateway-slctr-expr-notin-yes hosts: - - '*' + - '*.slctr-expr-notin-yes.example' http: - name: default/bind-all route: diff --git a/test/README.md b/test/README.md index c8dde1952..5f453c2c3 100644 --- a/test/README.md +++ b/test/README.md @@ -18,6 +18,7 @@ Higress e2e tests are mainly focusing on two parts for now: Higress provides make target to run ingress api conformance tests and wasmplugin tests, + API Tests: `make higress-conformance-test` ++ Gateway API Tests: `make gateway-conformance-test` + WasmPlugin Tests: `make higress-wasmplugin-test` + Build all Go WasmPlugins for testing: `make higress-wasmplugin-test` + Build tests for a specific Go WasmPlugin only: `PLUGIN_NAME=request-block make higress-wasmplugin-test` @@ -71,15 +72,6 @@ The test environment reusability is primarily achieved through the following tar ## Gateway APIs Conformance Tests -Gateway API Conformance tests are based on the suite provided by `kubernetes-sig/gateway-api`, we can reuse that, -and decide what conformance tests we need to open. Conformance tests of Gateway API. +Run `make gateway-conformance-test` to execute the upstream Gateway API v1.4.0 Conformance Suite. The default scope is the required `GATEWAY-HTTP` Core profile (`Gateway`, `HTTPRoute`, and `ReferenceGrant`); Extended features are not enabled. -This API covers a broad set of features and use cases and has been implemented widely. -This combination of both a large feature set and variety of implementations requires -clear conformance definitions and tests to ensure the API provides a consistent experience wherever it is used. - -Gateway API includes a set of conformance tests. These create a series of Gateways and Routes with the specified -GatewayClass, and test that the implementation matches the API specification. - -Each release contains a set of conformance tests, these will continue to expand as the API evolves. -Currently conformance tests cover the majority of Core capabilities in the standard channel, in addition to some Extended capabilities. +The runner imports the upstream suite and its embedded manifests directly, so Higress does not maintain copies of official test cases. Set `GATEWAY_CONFORMANCE_RUN_TEST=` only when debugging one upstream test. The default PR workflow always runs the complete Core profile and stores the generated report as a CI artifact. diff --git a/test/README_CN.md b/test/README_CN.md index f4d854d9b..492ec70eb 100644 --- a/test/README_CN.md +++ b/test/README_CN.md @@ -18,6 +18,7 @@ Higress E2E 测试主要关注两个部分: Higress 提供了运行 Ingress API 一致性测试和 wasmplugin 测试的 make 目标, + API 测试: `make higress-conformance-test` ++ Gateway API 测试: `make gateway-conformance-test` + WasmPlugin 测试: `make higress-wasmplugin-test` + 为测试构建所有 GO WasmPlugins: `make higress-wasmplugin-test` + 仅为一个 GO WasmPlugin 构建测试: `PLUGIN_NAME=request-block make higress-wasmplugin-test` @@ -70,12 +71,6 @@ Higress 提供了运行 Ingress API 一致性测试和 wasmplugin 测试的 make ## Gateway API 一致性测试 -Gateway API 一致性测试基于 `kubernetes-sig/gateway-api` 提供的套件,我们可以重复使用它,并决定我们需要打开哪些 Gateway API 的一致性测试。 +执行 `make gateway-conformance-test` 可运行上游 Gateway API v1.4.0 Conformance Suite。默认范围只包含 `GATEWAY-HTTP` Profile 的必选 Core 能力:`Gateway`、`HTTPRoute` 和 `ReferenceGrant`,不启用 Extended 能力。 -此 API 包含一系列广泛的功能和用例,并已得到广泛实现。 -这个大的功能集和各种实现的结合需要明确的一致性定义和测试,以确保在任何地方使用该 API 时都提供一致的体验。 - -Gateway API 包括一组一致性测试。这些测试创建具有指定 GatewayClass 的一系列 Gateways 和 Routes,并测试实现是否符合 API 规范。 - -每个发布版本都包含一组一致性测试,随着 API 的演进,这些测试将不断扩展。 -目前,一致性测试覆盖了标准通道中的大多数核心功能,以及一些扩展功能。 \ No newline at end of file +Runner 直接引用上游 Suite 及其内嵌 manifests,Higress 不复制维护官方测试用例。只有调试单个上游用例时才设置 `GATEWAY_CONFORMANCE_RUN_TEST=`;默认 PR 流水线始终执行完整 Core Profile,并将官方报告保存为 CI artifact。 diff --git a/test/gateway/e2e_test.go b/test/gateway/e2e_test.go index 11c3103d1..a0f464688 100644 --- a/test/gateway/e2e_test.go +++ b/test/gateway/e2e_test.go @@ -13,3 +13,46 @@ // limitations under the License. package gateway + +import ( + "context" + "net" + "os" + "testing" + + "sigs.k8s.io/gateway-api/conformance" + "sigs.k8s.io/gateway-api/conformance/utils/roundtripper" +) + +const dialLocalhostEnv = "HIGRESS_GATEWAY_API_TEST_DIAL_LOCALHOST" +const localHTTPPortEnv = "HIGRESS_GATEWAY_API_TEST_LOCAL_HTTP_PORT" +const localHTTPSPortEnv = "HIGRESS_GATEWAY_API_TEST_LOCAL_HTTPS_PORT" + +func TestGatewayAPIConformance(t *testing.T) { + opts := conformance.DefaultOptions(t) + if os.Getenv(dialLocalhostEnv) == "true" { + opts.RoundTripper = &roundtripper.DefaultRoundTripper{ + Debug: opts.Debug, + TimeoutConfig: opts.TimeoutConfig, + CustomDialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + _, port, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + switch port { + case "80": + if localPort := os.Getenv(localHTTPPortEnv); localPort != "" { + port = localPort + } + case "443": + if localPort := os.Getenv(localHTTPSPortEnv); localPort != "" { + port = localPort + } + } + var dialer net.Dialer + return dialer.DialContext(ctx, network, net.JoinHostPort("127.0.0.1", port)) + }, + } + } + conformance.RunConformanceWithOptions(t, opts) +} diff --git a/tools/hack/create-cluster.sh b/tools/hack/create-cluster.sh index d9e7907ca..a77497a6f 100755 --- a/tools/hack/create-cluster.sh +++ b/tools/hack/create-cluster.sh @@ -20,6 +20,7 @@ set -euo pipefail CLUSTER_NAME=${CLUSTER_NAME:-"higress"} METALLB_VERSION=${METALLB_VERSION:-"v0.13.7"} KIND_NODE_TAG=${KIND_NODE_TAG:-"v1.25.3"} +KIND=${KIND:-"tools/bin/kind"} PROJECT_DIR=$(pwd) echo ${KIND_NODE_TAG} @@ -67,7 +68,7 @@ EOF ## Create kind cluster. if [[ -z "${KIND_NODE_TAG}" ]]; then - tools/bin/kind create cluster --name "${CLUSTER_NAME}" --config=tools/hack/cluster.conf + "${KIND}" create cluster --name "${CLUSTER_NAME}" --config=tools/hack/cluster.conf else - tools/bin/kind create cluster --image "kindest/node:${KIND_NODE_TAG}" --name "${CLUSTER_NAME}" --config=tools/hack/cluster.conf + "${KIND}" create cluster --image "kindest/node:${KIND_NODE_TAG}" --name "${CLUSTER_NAME}" --config=tools/hack/cluster.conf fi diff --git a/tools/src/kind-gateway-api/go.mod b/tools/src/kind-gateway-api/go.mod new file mode 100644 index 000000000..a204da002 --- /dev/null +++ b/tools/src/kind-gateway-api/go.mod @@ -0,0 +1,20 @@ +module github.com/alibaba/higress/tools/src/kind-gateway-api + +go 1.24.0 + +require sigs.k8s.io/kind v0.30.0 + +require ( + al.essio.dev/pkg/shellescape v1.5.1 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/sys v0.6.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/tools/src/kind-gateway-api/go.sum b/tools/src/kind-gateway-api/go.sum new file mode 100644 index 000000000..bd562d22b --- /dev/null +++ b/tools/src/kind-gateway-api/go.sum @@ -0,0 +1,42 @@ +al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho= +al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= +sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/tools/src/kind-gateway-api/pin.go b/tools/src/kind-gateway-api/pin.go new file mode 100644 index 000000000..9f33c4862 --- /dev/null +++ b/tools/src/kind-gateway-api/pin.go @@ -0,0 +1,19 @@ +// Copyright (c) 2026 Alibaba Group Holding Ltd. +// +// 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. + +//go:build pin + +package ignore + +import "sigs.k8s.io/kind" diff --git a/tools/tools.mk b/tools/tools.mk index 3785e8ec0..6ef35f37a 100644 --- a/tools/tools.mk +++ b/tools/tools.mk @@ -16,6 +16,7 @@ tools/controller-gen = $(tools.bindir)/controller-gen tools/golangci-lint = $(tools.bindir)/golangci-lint tools/kustomize = $(tools.bindir)/kustomize tools/kind = $(tools.bindir)/kind +tools/kind-gateway-api = $(tools.bindir)/kind-gateway-api tools/setup-envtest = $(tools.bindir)/setup-envtest $(tools.bindir)/%: $(tools.srcdir)/%/pin.go $(tools.srcdir)/%/go.mod cd $(