From 5e0855ecc7c5e9422cc7d6df565ca12523346b94 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 22 Jan 2024 15:30:05 +0100 Subject: [PATCH] feat: adds conformance test gateway api --- .github/workflows/test-conformance.yaml | 37 +++ .github/workflows/test-integration.yaml | 3 - .gitignore | 1 + Makefile | 5 + go.mod | 10 +- go.sum | 24 +- .../fixtures/k8s_gateway_conformance.toml | 21 ++ integration/integration_test.go | 6 +- integration/k8s_conformance_test.go | 229 ++++++++++++++++++ pkg/provider/kubernetes/gateway/kubernetes.go | 12 +- 10 files changed, 333 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/test-conformance.yaml create mode 100644 integration/fixtures/k8s_gateway_conformance.toml create mode 100644 integration/k8s_conformance_test.go diff --git a/.github/workflows/test-conformance.yaml b/.github/workflows/test-conformance.yaml new file mode 100644 index 000000000..70e2cbe7c --- /dev/null +++ b/.github/workflows/test-conformance.yaml @@ -0,0 +1,37 @@ +name: Test K8s Gateway API conformance + +on: + pull_request: + branches: + - '*' + paths: + - 'pkg/provider/kubernetes/gateway' + +env: + GO_VERSION: '1.21' + CGO_ENABLED: 0 + +jobs: + + test-conformance: + runs-on: ubuntu-20.04 + + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Avoid generating webui + run: touch webui/static/index.html + + - name: Build binary + run: make binary + + - name: K8s Gateway API conformance test + run: sudo make test-gateway-api-conformance diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 5155fa78f..0625da388 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -4,9 +4,6 @@ on: pull_request: branches: - '*' - push: - branches: - - 'gh-actions' env: GO_VERSION: '1.21' diff --git a/.gitignore b/.gitignore index 3fd473f24..03a5e9369 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ plugins-storage/ plugins-local/ traefik_changelog.md integration/tailscale.secret +integration/conformance-reports/ diff --git a/Makefile b/Makefile index 15565a3ce..93498e5c9 100644 --- a/Makefile +++ b/Makefile @@ -99,6 +99,11 @@ test-unit: test-integration: binary GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -test.timeout=20m -failfast -v $(TESTFLAGS) +## Run the conformance tests +.PHONY: test-gateway-api-conformance +test-gateway-api-conformance: binary + GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance=true $(TESTFLAGS) + ## Pull all Docker images to avoid timeout during integration tests .PHONY: pull-images pull-images: diff --git a/go.mod b/go.mod index 1e3228e15..17fee2a7f 100644 --- a/go.mod +++ b/go.mod @@ -85,12 +85,13 @@ require ( golang.org/x/tools v0.14.0 google.golang.org/grpc v1.59.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.28.3 + k8s.io/api v0.28.4 k8s.io/apiextensions-apiserver v0.28.3 - k8s.io/apimachinery v0.28.3 - k8s.io/client-go v0.28.3 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 k8s.io/utils v0.0.0-20230726121419-3b25d923346b mvdan.cc/xurls/v2 v2.5.0 + sigs.k8s.io/controller-runtime v0.16.3 sigs.k8s.io/gateway-api v1.0.0 ) @@ -162,6 +163,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.7.0 // indirect github.com/exoscale/egoscale v0.100.1 // indirect github.com/fatih/color v1.15.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -236,6 +238,7 @@ require ( github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -254,7 +257,6 @@ require ( github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/onsi/gomega v1.27.10 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.5 // indirect diff --git a/go.sum b/go.sum index 9c0c8a0eb..28bc21641 100644 --- a/go.sum +++ b/go.sum @@ -114,6 +114,8 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -302,6 +304,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= +github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exoscale/egoscale v0.100.1 h1:iXsV1Ei7daqe/6FYSCSDyrFs1iUG1l1X9qNh2uMw6z0= github.com/exoscale/egoscale v0.100.1/go.mod h1:BAb9p4rmyU+Wl400CJZO5270H2sXtdsZjLcm5xMKkz4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -358,6 +362,8 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -796,6 +802,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= @@ -1485,6 +1493,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= @@ -1619,14 +1629,14 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= -k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= -k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= -k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= -k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= -k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= @@ -1640,6 +1650,8 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/integration/fixtures/k8s_gateway_conformance.toml b/integration/fixtures/k8s_gateway_conformance.toml new file mode 100644 index 000000000..5114f2dfb --- /dev/null +++ b/integration/fixtures/k8s_gateway_conformance.toml @@ -0,0 +1,21 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + noColor = true + +[api] + insecure = true + +[experimental] + kubernetesGateway = true + +[entryPoints] + [entryPoints.web] + address = ":80" + [entryPoints.websecure] + address = ":443" + +[providers.kubernetesGateway] diff --git a/integration/integration_test.go b/integration/integration_test.go index 0e9d22d78..51df6870c 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -35,7 +35,11 @@ import ( "gopkg.in/yaml.v3" ) -var showLog = flag.Bool("tlog", false, "always show Traefik logs") +var ( + showLog = flag.Bool("tlog", false, "always show Traefik logs") + k8sConformance = flag.Bool("k8sConformance", false, "run K8s Gateway API conformance test") + k8sConformanceRunTest = flag.String("k8sConformanceRunTest", "", "run a specific K8s Gateway API conformance test") +) const tailscaleSecretFilePath = "tailscale.secret" diff --git a/integration/k8s_conformance_test.go b/integration/k8s_conformance_test.go new file mode 100644 index 000000000..f5c869f6c --- /dev/null +++ b/integration/k8s_conformance_test.go @@ -0,0 +1,229 @@ +package integration + +import ( + "context" + "fmt" + "net" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/traefik/traefik/v3/integration/try" + "github.com/traefik/traefik/v3/pkg/version" + "gopkg.in/yaml.v3" + ktypes "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + kclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" + gatev1 "sigs.k8s.io/gateway-api/apis/v1" + gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + conformanceV1alpha1 "sigs.k8s.io/gateway-api/conformance/apis/v1alpha1" + "sigs.k8s.io/gateway-api/conformance/tests" + "sigs.k8s.io/gateway-api/conformance/utils/config" + ksuite "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +// K8sConformanceSuite tests suite. +type K8sConformanceSuite struct{ BaseSuite } + +func TestK8sConformanceSuite(t *testing.T) { + suite.Run(t, new(K8sConformanceSuite)) +} + +func (s *K8sConformanceSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + + s.createComposeProject("k8s") + s.composeUp() + + abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml") + require.NoError(s.T(), err) + + err = try.Do(60*time.Second, func() error { + _, err := os.Stat(abs) + return err + }) + require.NoError(s.T(), err) + + data, err := os.ReadFile(abs) + require.NoError(s.T(), err) + + content := strings.ReplaceAll(string(data), "https://server:6443", fmt.Sprintf("https://%s", net.JoinHostPort(s.getComposeServiceIP("server"), "6443"))) + + err = os.WriteFile(abs, []byte(content), 0o644) + require.NoError(s.T(), err) + + err = os.Setenv("KUBECONFIG", abs) + require.NoError(s.T(), err) +} + +func (s *K8sConformanceSuite) TearDownSuite() { + s.BaseSuite.TearDownSuite() + + generatedFiles := []string{ + "./fixtures/k8s/config.skip/kubeconfig.yaml", + "./fixtures/k8s/config.skip/k3s.log", + "./fixtures/k8s/rolebindings.yaml", + "./fixtures/k8s/ccm.yaml", + } + + for _, filename := range generatedFiles { + if err := os.Remove(filename); err != nil { + log.Warn().Err(err).Send() + } + } +} + +func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { + if !*k8sConformance { + s.T().Skip("Skip because it can take a long time to execute. To enable pass the `k8sConformance` flag.") + } + + configFromFlags, err := clientcmd.BuildConfigFromFlags("", os.Getenv("KUBECONFIG")) + if err != nil { + s.T().Fatal(err) + } + + kClient, err := client.New(configFromFlags, client.Options{}) + if err != nil { + s.T().Fatalf("Error initializing Kubernetes client: %v", err) + } + + kClientSet, err := kclientset.NewForConfig(configFromFlags) + if err != nil { + s.T().Fatal(err) + } + + err = gatev1alpha2.AddToScheme(kClient.Scheme()) + require.NoError(s.T(), err) + err = gatev1beta1.AddToScheme(kClient.Scheme()) + require.NoError(s.T(), err) + err = gatev1.AddToScheme(kClient.Scheme()) + require.NoError(s.T(), err) + + s.traefikCmd(withConfigFile("fixtures/k8s_gateway_conformance.toml")) + + // Wait for traefik to start + err = try.GetRequest("http://127.0.0.1:8080/api/entrypoints", 10*time.Second, try.BodyContains(`"name":"web"`)) + require.NoError(s.T(), err) + + err = try.Do(10*time.Second, func() error { + gwc := &gatev1.GatewayClass{} + err := kClient.Get(context.Background(), ktypes.NamespacedName{Name: "my-gateway-class"}, gwc) + if err != nil { + return fmt.Errorf("error fetching GatewayClass: %w", err) + } + + return nil + }) + require.NoError(s.T(), err) + + opts := ksuite.Options{ + Client: kClient, + RestConfig: configFromFlags, + Clientset: kClientSet, + GatewayClassName: "my-gateway-class", + Debug: true, + CleanupBaseResources: true, + TimeoutConfig: config.TimeoutConfig{ + CreateTimeout: 5 * time.Second, + DeleteTimeout: 5 * time.Second, + GetTimeout: 5 * time.Second, + GatewayMustHaveAddress: 5 * time.Second, + GatewayMustHaveCondition: 5 * time.Second, + GatewayStatusMustHaveListeners: 10 * time.Second, + GatewayListenersMustHaveCondition: 5 * time.Second, + GWCMustBeAccepted: 60 * time.Second, // Pod creation in k3s cluster can be long. + HTTPRouteMustNotHaveParents: 5 * time.Second, + HTTPRouteMustHaveCondition: 5 * time.Second, + TLSRouteMustHaveCondition: 5 * time.Second, + RouteMustHaveParents: 5 * time.Second, + ManifestFetchTimeout: 5 * time.Second, + MaxTimeToConsistency: 5 * time.Second, + NamespacesMustBeReady: 60 * time.Second, // Pod creation in k3s cluster can be long. + RequestTimeout: 5 * time.Second, + LatestObservedGenerationSet: 5 * time.Second, + RequiredConsecutiveSuccesses: 0, + }, + SupportedFeatures: sets.New[ksuite.SupportedFeature](). + Insert(ksuite.GatewayCoreFeatures.UnsortedList()...), + EnableAllSupportedFeatures: false, + RunTest: *k8sConformanceRunTest, + // Until the feature are all supported, following tests are skipped. + SkipTests: []string{ + "HTTPExactPathMatching", + "HTTPRouteHostnameIntersection", + "GatewaySecretReferenceGrantAllInNamespace", + "HTTPRouteListenerHostnameMatching", + "HTTPRouteRequestHeaderModifier", + "GatewaySecretInvalidReferenceGrant", + "GatewayClassObservedGenerationBump", + "HTTPRouteInvalidNonExistentBackendRef", + "GatewayWithAttachedRoutes", + "HTTPRouteCrossNamespace", + "HTTPRouteDisallowedKind", + "HTTPRouteInvalidReferenceGrant", + "HTTPRouteObservedGenerationBump", + "GatewayInvalidRouteKind", + "TLSRouteSimpleSameNamespace", + "TLSRouteInvalidReferenceGrant", + "HTTPRouteInvalidCrossNamespaceParentRef", + "HTTPRouteInvalidParentRefNotMatchingSectionName", + "GatewaySecretReferenceGrantSpecific", + "GatewayModifyListeners", + "GatewaySecretMissingReferenceGrant", + "GatewayInvalidTLSConfiguration", + "HTTPRouteInvalidCrossNamespaceBackendRef", + "HTTPRouteMatchingAcrossRoutes", + "HTTPRoutePartiallyInvalidViaInvalidReferenceGrant", + "HTTPRouteRedirectHostAndStatus", + "HTTPRouteInvalidBackendRefUnknownKind", + "HTTPRoutePathMatchOrder", + "HTTPRouteSimpleSameNamespace", + "HTTPRouteMatching", + "HTTPRouteHeaderMatching", + "HTTPRouteReferenceGrant", + }, + } + + cSuite, err := ksuite.NewExperimentalConformanceTestSuite(ksuite.ExperimentalConformanceOptions{ + Options: opts, + Implementation: conformanceV1alpha1.Implementation{ + Organization: "traefik", + Project: "traefik", + URL: "https://traefik.io/", + Version: version.Version, + Contact: []string{"@traefik/maintainers"}, + }, + ConformanceProfiles: sets.New[ksuite.ConformanceProfileName]( + ksuite.HTTPConformanceProfileName, + ksuite.TLSConformanceProfileName, + ), + }) + require.NoError(s.T(), err) + + cSuite.Setup(s.T()) + err = cSuite.Run(s.T(), tests.ConformanceTests) + require.NoError(s.T(), err) + + report, err := cSuite.Report() + require.NoError(s.T(), err, "failed generating conformance report") + + report.GatewayAPIVersion = "1.0.0" + + rawReport, err := yaml.Marshal(report) + require.NoError(s.T(), err) + s.T().Logf("Conformance report:\n%s", string(rawReport)) + + require.NoError(s.T(), os.MkdirAll("./conformance-reports", 0o755)) + outFile := filepath.Join("conformance-reports", fmt.Sprintf("traefik-traefik-%d.yaml", time.Now().UnixNano())) + require.NoError(s.T(), os.WriteFile(outFile, rawReport, 0o600)) + s.T().Logf("Report written to: %s", outFile) +} diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index c86467b0f..e9ed7e6a7 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -230,6 +230,7 @@ func (p *Provider) loadConfigurationFromGateway(ctx context.Context, client Clie err := client.UpdateGatewayClassStatus(gatewayClass, metav1.Condition{ Type: string(gatev1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, + ObservedGeneration: gatewayClass.Generation, Reason: "Handled", Message: "Handled by Traefik controller", LastTransitionTime: metav1.Now(), @@ -587,7 +588,16 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [ Type: string(gatev1.GatewayConditionAccepted), Status: metav1.ConditionTrue, ObservedGeneration: gateway.Generation, - Reason: string(gatev1.GatewayConditionAccepted), + Reason: string(gatev1.GatewayReasonAccepted), + Message: "Gateway successfully scheduled", + LastTransitionTime: metav1.Now(), + }, + // update "Programmed" status with "Programmed" reason + metav1.Condition{ + Type: string(gatev1.GatewayConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + Reason: string(gatev1.GatewayReasonProgrammed), Message: "Gateway successfully scheduled", LastTransitionTime: metav1.Now(), },