diff --git a/.gitignore b/.gitignore index 857cb4a..33701ec 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ hack/dev/cloudflare-api.yaml cover.out .idea/ + +gateway-api-conformance-test-report.yaml \ No newline at end of file diff --git a/cmd/cloudflare-tunnel-ingress-controller/main.go b/cmd/cloudflare-tunnel-ingress-controller/main.go index 750bdb1..0647112 100644 --- a/cmd/cloudflare-tunnel-ingress-controller/main.go +++ b/cmd/cloudflare-tunnel-ingress-controller/main.go @@ -2,18 +2,22 @@ package main import ( "context" + "log" + "os" + "time" + cloudflarecontroller "github.com/STRRL/cloudflare-tunnel-ingress-controller/pkg/cloudflare-controller" "github.com/STRRL/cloudflare-tunnel-ingress-controller/pkg/controller" "github.com/cloudflare/cloudflare-go" "github.com/go-logr/logr" "github.com/go-logr/stdr" "github.com/spf13/cobra" - "log" - "os" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/config" crlog "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/manager" - "time" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) type rootCmdFlags struct { @@ -72,7 +76,20 @@ func main() { os.Exit(1) } - mgr, err := manager.New(cfg, manager.Options{}) + scheme := runtime.NewScheme() + err = clientgoscheme.AddToScheme(scheme) + if err != nil { + logger.Error(err, "unable to add scheme") + os.Exit(1) + } + // append gateway-api scheme + err = gatewayv1.AddToScheme(scheme) + if err != nil { + logger.Error(err, "unable to add gateway-api scheme") + os.Exit(1) + } + + mgr, err := manager.New(cfg, manager.Options{Scheme: scheme}) if err != nil { logger.Error(err, "unable to set up manager") os.Exit(1) @@ -89,6 +106,17 @@ func main() { return err } + err = controller.RegisterGatewayClassController(logger, mgr) + if err != nil { + return err + } + + // Add this new registration for the Gateway controller + err = controller.RegisterGatewayController(logger, mgr) + if err != nil { + return err + } + ticker := time.NewTicker(10 * time.Second) done := make(chan struct{}) defer close(done) diff --git a/go.mod b/go.mod index 9b4e1b2..ad2b627 100644 --- a/go.mod +++ b/go.mod @@ -12,23 +12,26 @@ require ( github.com/onsi/gomega v1.34.2 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.9.0 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 sigs.k8s.io/controller-runtime v0.19.0 + sigs.k8s.io/gateway-api v1.1.0 + sigs.k8s.io/yaml v1.4.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -40,14 +43,19 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/miekg/dns v1.1.58 // indirect + github.com/moby/spdystream v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect @@ -55,23 +63,26 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index aec97ed..f096036 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +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/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -5,15 +7,14 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cloudflare/cloudflare-go v0.103.0 h1:XXKzgXeUbAo7UTtM4T5wuD2bJPBtNZv7TlZAEy5QI4k= github.com/cloudflare/cloudflare-go v0.103.0/go.mod h1:0DrjT4g8wgYFYIxhlqR8xi8dNWfyHFGilUkU3+XV8h0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -27,13 +28,12 @@ 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.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= @@ -59,8 +59,10 @@ github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSF github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -69,15 +71,16 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -85,6 +88,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= @@ -110,12 +115,7 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -135,6 +135,8 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -146,6 +148,8 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -171,6 +175,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T 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= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -183,7 +191,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= @@ -196,12 +203,14 @@ k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 h1:Q8Z7VlGhcJgBHJHYugJ/K/7iB8a2eSxCyxdVjJp+lLY= +k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM= +sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/hack/dev/deployment.yaml b/hack/dev/deployment.yaml index 80d64a5..dffd6e2 100644 --- a/hack/dev/deployment.yaml +++ b/hack/dev/deployment.yaml @@ -11,74 +11,6 @@ spec: selector: app: cloudflare-tunnel-ingress-controller --- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: cloudflare-tunnel-ingress-controller - labels: - app: cloudflare-tunnel-ingress-controller -rules: - - apiGroups: - - "" - resources: - - services - - endpoints - - secrets - verbs: - - get - - list - - watch - - apiGroups: - - networking.k8s.io - resources: - - ingresses - - ingressclasses - verbs: - - get - - list - - watch - - update - - apiGroups: - - networking.k8s.io - resources: - - ingresses/status - verbs: - - update - - apiGroups: - - apps - resources: - - deployments - verbs: - - get - - list - - watch - - update - - create ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cloudflare-tunnel-ingress-controller - namespace: cloudflare-tunnel-ingress-controller-dev - labels: - app: cloudflare-tunnel-ingress-controller ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: cloudflare-tunnel-ingress-controller - labels: - app: cloudflare-tunnel-ingress-controller -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cloudflare-tunnel-ingress-controller -subjects: - - name: cloudflare-tunnel-ingress-controller - kind: ServiceAccount - # hardcoded namespace for dev - namespace: cloudflare-tunnel-ingress-controller-dev ---- apiVersion: apps/v1 kind: Deployment metadata: @@ -132,4 +64,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: CLOUDFLARED_IMAGE + value: "cloudflare/cloudflared:latest" + - name: CLOUDFLARED_IMAGE_PULL_POLICY + value: "IfNotPresent" + - name: CLOUDFLARED_REPLICA_COUNT + value: "1" serviceAccountName: cloudflare-tunnel-ingress-controller diff --git a/hack/dev/gatewayclass.yaml b/hack/dev/gatewayclass.yaml new file mode 100644 index 0000000..d0c0b13 --- /dev/null +++ b/hack/dev/gatewayclass.yaml @@ -0,0 +1,6 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: cloudflare-tunnel +spec: + controllerName: "strrl.dev/cloudflare-tunnel-gatewayclass-controller" diff --git a/hack/dev/rbac.yaml b/hack/dev/rbac.yaml new file mode 100644 index 0000000..ab03f15 --- /dev/null +++ b/hack/dev/rbac.yaml @@ -0,0 +1,81 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cloudflare-tunnel-ingress-controller + labels: + app: cloudflare-tunnel-ingress-controller +rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + - ingressclasses + verbs: + - get + - list + - watch + - update + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch + - update + - create + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + - gatewayclasses/status + - gateways + - gateways/status + verbs: + - get + - list + - watch + - update + - patch +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cloudflare-tunnel-ingress-controller + namespace: cloudflare-tunnel-ingress-controller-dev + labels: + app: cloudflare-tunnel-ingress-controller +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cloudflare-tunnel-ingress-controller + labels: + app: cloudflare-tunnel-ingress-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cloudflare-tunnel-ingress-controller +subjects: + - name: cloudflare-tunnel-ingress-controller + kind: ServiceAccount + # hardcoded namespace for dev + namespace: cloudflare-tunnel-ingress-controller-dev diff --git a/helm/cloudflare-tunnel-ingress-controller/templates/clusterrole.yaml b/helm/cloudflare-tunnel-ingress-controller/templates/clusterrole.yaml index e3ce1fc..5306200 100644 --- a/helm/cloudflare-tunnel-ingress-controller/templates/clusterrole.yaml +++ b/helm/cloudflare-tunnel-ingress-controller/templates/clusterrole.yaml @@ -38,4 +38,17 @@ rules: - list - watch - update - - create \ No newline at end of file + - create + - apiGroups: + - gateway.networking.k8s.io + resources: + - gatewayclasses + - gatewayclasses/status + - gateways + - gateways/status + verbs: + - get + - list + - watch + - update + - patch diff --git a/helm/cloudflare-tunnel-ingress-controller/templates/gatewayclass.yaml b/helm/cloudflare-tunnel-ingress-controller/templates/gatewayclass.yaml new file mode 100644 index 0000000..d0c0b13 --- /dev/null +++ b/helm/cloudflare-tunnel-ingress-controller/templates/gatewayclass.yaml @@ -0,0 +1,6 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: cloudflare-tunnel +spec: + controllerName: "strrl.dev/cloudflare-tunnel-gatewayclass-controller" diff --git a/image/cloudflare-tunnel-ingress-controller/Dockerfile b/image/cloudflare-tunnel-ingress-controller/Dockerfile index 64c6380..8c5a4a2 100644 --- a/image/cloudflare-tunnel-ingress-controller/Dockerfile +++ b/image/cloudflare-tunnel-ingress-controller/Dockerfile @@ -11,7 +11,7 @@ RUN go mod download && go mod verify COPY . . RUN --mount=type=cache,target=/go \ CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH GO111MODULE=on \ - go build -ldflags="-s -w" -o cloudflare-tunnel-ingress-controller ./cmd/cloudflare-tunnel-ingress-controller + go build -o cloudflare-tunnel-ingress-controller ./cmd/cloudflare-tunnel-ingress-controller # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/pkg/controller/bootstrap.go b/pkg/controller/bootstrap.go index 1dd69ee..0daa6ea 100644 --- a/pkg/controller/bootstrap.go +++ b/pkg/controller/bootstrap.go @@ -6,6 +6,7 @@ import ( networkingv1 "k8s.io/api/networking/v1" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/manager" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) type IngressControllerOptions struct { @@ -26,8 +27,33 @@ func RegisterIngressController(logger logr.Logger, mgr manager.Manager, options return err } + return nil +} + +func RegisterGatewayClassController(logger logr.Logger, mgr manager.Manager) error { + controller := NewGatewayClassController(logger.WithName("gatewayclass-controller"), mgr.GetClient()) + err := builder. + ControllerManagedBy(mgr). + For(&gatewayv1.GatewayClass{}). + Complete(controller) + + if err != nil { + logger.WithName("register-controller").Error(err, "could not register gatewayclass controller") + return err + } + + return nil +} + +func RegisterGatewayController(logger logr.Logger, mgr manager.Manager) error { + controller := NewGatewayController(logger.WithName("gateway-controller"), mgr.GetClient()) + err := builder. + ControllerManagedBy(mgr). + For(&gatewayv1.Gateway{}). + Complete(controller) + if err != nil { - logger.WithName("register-controller").Error(err, "could not register ingress class controller") + logger.WithName("register-controller").Error(err, "could not register gateway controller") return err } diff --git a/pkg/controller/gateway-controller.go b/pkg/controller/gateway-controller.go new file mode 100644 index 0000000..5984b19 --- /dev/null +++ b/pkg/controller/gateway-controller.go @@ -0,0 +1,141 @@ +package controller + +import ( + "context" + + "github.com/go-logr/logr" + "github.com/pkg/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +var _ reconcile.Reconciler = &GatewayController{} + +type GatewayController struct { + logger logr.Logger + kubeClient client.Client +} + +func NewGatewayController(logger logr.Logger, kubeClient client.Client) *GatewayController { + return &GatewayController{logger: logger, kubeClient: kubeClient} +} + +func (g *GatewayController) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + gateway := gatewayv1.Gateway{} + err := g.kubeClient.Get(ctx, request.NamespacedName, &gateway) + if err != nil { + if apierrors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, errors.Wrapf(err, "fetch gateway %s", request.NamespacedName) + } + + // Check if the Gateway is associated with our GatewayClass + isOurs, err := g.isOurGatewayClass(ctx, gateway) + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "failed to check if Gateway is associated with our GatewayClass") + } + if !isOurs { + g.logger.V(1).Info("Gateway is not associated with our GatewayClass", "gateway", request.NamespacedName) + return reconcile.Result{}, nil + } + + g.logger.V(1).Info("Processing Gateway associated with our GatewayClass", "gateway", request.NamespacedName) + + // Add your custom logic here for handling Gateway resources + + // Update Gateway status + if err := g.updateGatewayStatus(ctx, &gateway); err != nil { + return reconcile.Result{}, errors.Wrapf(err, "update status for gateway %s", request.NamespacedName) + } + + g.logger.V(3).Info("reconcile completed", "triggered-by", request.NamespacedName) + return reconcile.Result{}, nil +} + +func (g *GatewayController) updateGatewayStatus(ctx context.Context, gateway *gatewayv1.Gateway) error { + // Update Gateway conditions + meta.SetStatusCondition(&gateway.Status.Conditions, metav1.Condition{ + Type: string(gatewayv1.GatewayConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatewayv1.GatewayReasonProgrammed), + Message: "Gateway has been programmed", + }) + meta.SetStatusCondition(&gateway.Status.Conditions, metav1.Condition{ + Type: string(gatewayv1.GatewayConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatewayv1.GatewayReasonAccepted), + Message: "Gateway has been programmed", + }) + + // Update existing listener statuses + var updatedListeners []gatewayv1.ListenerStatus + for _, listener := range gateway.Spec.Listeners { + listenerStatus := gatewayv1.ListenerStatus{ + Name: listener.Name, + SupportedKinds: []gatewayv1.RouteGroupKind{}, + } + + allKindsSupported := true + for _, kind := range listener.AllowedRoutes.Kinds { + if kind.Kind == "HTTPRoute" { + listenerStatus.SupportedKinds = append(listenerStatus.SupportedKinds, gatewayv1.RouteGroupKind{ + Group: (*gatewayv1.Group)(&gatewayv1.GroupVersion.Group), + Kind: "HTTPRoute", + }) + } else { + allKindsSupported = false + } + } + + // Set ListenerConditionResolvedRefs based on supported kinds + if allKindsSupported { + meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ + Type: string(gatewayv1.ListenerConditionResolvedRefs), + Status: metav1.ConditionTrue, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatewayv1.ListenerReasonResolvedRefs), + Message: "All route kinds are supported", + }) + } else { + meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ + Type: string(gatewayv1.ListenerConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: gateway.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatewayv1.ListenerReasonInvalidRouteKinds), + Message: "Some route kinds are not supported", + }) + } + + updatedListeners = append(updatedListeners, listenerStatus) + } + + // Update the Gateway status with the filtered listeners + gateway.Status.Listeners = updatedListeners + // Update the Gateway status + return g.kubeClient.Status().Update(ctx, gateway) +} + +func (g *GatewayController) isOurGatewayClass(ctx context.Context, gateway gatewayv1.Gateway) (bool, error) { + gatewayClass := gatewayv1.GatewayClass{} + err := g.kubeClient.Get(ctx, client.ObjectKey{Name: string(gateway.Spec.GatewayClassName)}, &gatewayClass) + if err != nil { + if apierrors.IsNotFound(err) { + g.logger.V(1).Info("GatewayClass not found", "gatewayClassName", gateway.Spec.GatewayClassName) + return false, nil + } + return false, errors.Wrapf(err, "failed to get GatewayClass %s", gateway.Spec.GatewayClassName) + } + + return string(gatewayClass.Spec.ControllerName) == ControllerName, nil +} diff --git a/pkg/controller/gatewayclass-controller.go b/pkg/controller/gatewayclass-controller.go new file mode 100644 index 0000000..94abb14 --- /dev/null +++ b/pkg/controller/gatewayclass-controller.go @@ -0,0 +1,133 @@ +package controller + +import ( + "context" + + "github.com/go-logr/logr" + "github.com/pkg/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// GatewayClassController should implement the Reconciler interface +var _ reconcile.Reconciler = &GatewayClassController{} + +const ( + GatewayClassControllerFinalizer = "strrl.dev/cloudflare-tunnel-gatewayclass-controller-controlled" + ControllerName = "strrl.dev/cloudflare-tunnel-gatewayclass-controller" +) + +type GatewayClassController struct { + logger logr.Logger + kubeClient client.Client +} + +func NewGatewayClassController(logger logr.Logger, kubeClient client.Client) *GatewayClassController { + return &GatewayClassController{logger: logger, kubeClient: kubeClient} +} + +func (g *GatewayClassController) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { + origin := gatewayv1.GatewayClass{} + err := g.kubeClient.Get(ctx, request.NamespacedName, &origin) + if err != nil { + if apierrors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, errors.Wrapf(err, "fetch gatewayclass %s", request.NamespacedName) + } + + controlled, err := g.isControlledByThisController(ctx, origin) + if err != nil && !apierrors.IsNotFound(err) { + return reconcile.Result{}, errors.Wrapf(err, "check if gatewayclass %s is controlled by this controller", request.NamespacedName) + } + + if !controlled { + g.logger.V(1).Info("gatewayclass is NOT controlled by this controller", + "gatewayclass", request.NamespacedName, + "controlled-controller-name", ControllerName, + ) + return reconcile.Result{ + Requeue: false, + }, nil + } + + g.logger.V(1).Info("gatewayclass is controlled by this controller", + "gatewayclass", request.NamespacedName, + "controlled-controller-name", ControllerName, + ) + + // Update GatewayClass status + if err := g.updateGatewayClassStatus(ctx, &origin); err != nil { + return reconcile.Result{}, errors.Wrapf(err, "update status for gatewayclass %s", request.NamespacedName) + } + + g.logger.Info("update cloudflare tunnel config", "triggered-by", request.NamespacedName) + + err = g.attachFinalizer(ctx, *(origin.DeepCopy())) + if err != nil { + return reconcile.Result{}, errors.Wrapf(err, "attach finalizer to gatewayclass %s", request.NamespacedName) + } + + // Add your custom logic here + + if origin.DeletionTimestamp != nil { + err = g.cleanFinalizer(ctx, origin) + if err != nil { + return reconcile.Result{}, errors.Wrapf(err, "clean finalizer from gatewayclass %s", request.NamespacedName) + } + } + + g.logger.V(3).Info("reconcile completed", "triggered-by", request.NamespacedName) + return reconcile.Result{}, nil +} + +func (g *GatewayClassController) isControlledByThisController(ctx context.Context, target gatewayv1.GatewayClass) (bool, error) { + if string(target.Spec.ControllerName) == ControllerName { + return true, nil + } + return false, nil +} + +func (g *GatewayClassController) attachFinalizer(ctx context.Context, gatewayClass gatewayv1.GatewayClass) error { + if stringSliceContains(gatewayClass.Finalizers, GatewayClassControllerFinalizer) { + return nil + } + gatewayClass.Finalizers = append(gatewayClass.Finalizers, GatewayClassControllerFinalizer) + err := g.kubeClient.Update(ctx, &gatewayClass) + if err != nil { + return errors.Wrapf(err, "attach finalizer for %s", gatewayClass.Name) + } + return nil +} + +func (g *GatewayClassController) cleanFinalizer(ctx context.Context, gatewayClass gatewayv1.GatewayClass) error { + if !stringSliceContains(gatewayClass.Finalizers, GatewayClassControllerFinalizer) { + return nil + } + gatewayClass.Finalizers = removeStringFromSlice(gatewayClass.Finalizers, GatewayClassControllerFinalizer) + err := g.kubeClient.Update(ctx, &gatewayClass) + if err != nil { + return errors.Wrapf(err, "clean finalizer for %s", gatewayClass.Name) + } + return nil +} + +// Update the updateGatewayClassStatus method +func (g *GatewayClassController) updateGatewayClassStatus(ctx context.Context, gatewayClass *gatewayv1.GatewayClass) error { + newCondition := metav1.Condition{ + Type: string(gatewayv1.GatewayClassConditionStatusAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: gatewayClass.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatewayv1.GatewayClassReasonAccepted), + Message: "GatewayClass has been accepted by the controller", + } + + meta.SetStatusCondition(&gatewayClass.Status.Conditions, newCondition) + + return g.kubeClient.Status().Update(ctx, gatewayClass) +} diff --git a/skaffold.yaml b/skaffold.yaml index 6f5ba8b..03c1fec 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -17,4 +17,8 @@ manifests: - hack/dev/ns.yaml - hack/dev/cloudflare-api.yaml - hack/dev/deployment.yaml + - hack/dev/gatewayclass.yaml - hack/dev/ingress-class.yaml + - hack/dev/rbac.yaml # Add this line +deploy: + kubectl: {} diff --git a/test/conformance/gateway_conformance_test.go b/test/conformance/gateway_conformance_test.go new file mode 100644 index 0000000..f933c7f --- /dev/null +++ b/test/conformance/gateway_conformance_test.go @@ -0,0 +1,48 @@ +package conformance + +import ( + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/util/sets" + "os" + "sigs.k8s.io/gateway-api/conformance" + conformancev1 "sigs.k8s.io/gateway-api/conformance/apis/v1" + "sigs.k8s.io/gateway-api/conformance/tests" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + "sigs.k8s.io/gateway-api/pkg/features" + "sigs.k8s.io/yaml" + "testing" +) + +func TestGatewayConformance(t *testing.T) { + var features []features.SupportedFeature + + opts := conformance.DefaultOptions(t) + opts.GatewayClassName = "cloudflare-tunnel" + opts.Debug = true + opts.SupportedFeatures = sets.New(features...) + opts.ConformanceProfiles = sets.New( + suite.GatewayHTTPConformanceProfileName, + ) + opts.Implementation = conformancev1.Implementation{ + Organization: "strrl", + Project: "cloudflare-tunnel-ingress-controller", + URL: "https://github.com/STRRL/cloudflare-tunnel-ingress-controller", + Version: "latest", + } + + testSuite, err := suite.NewConformanceTestSuite(opts) + require.NoError(t, err) + + testSuite.Setup(t, tests.ConformanceTests) + err = testSuite.Run(t, tests.ConformanceTests) + require.NoError(t, err) + + report, err := testSuite.Report() + require.NoError(t, err) + + bytes, err := yaml.Marshal(report) + require.NoError(t, err) + + err = os.WriteFile("gateway-api-conformance-test-report.yaml", bytes, 0o600) + require.NoError(t, err) +} diff --git a/test/conformance/suite_test.go b/test/conformance/suite_test.go new file mode 100644 index 0000000..df6f154 --- /dev/null +++ b/test/conformance/suite_test.go @@ -0,0 +1 @@ +package conformance diff --git a/test/integration/controller/suite_test.go b/test/integration/controller/suite_test.go index c73ec76..acc2a1f 100644 --- a/test/integration/controller/suite_test.go +++ b/test/integration/controller/suite_test.go @@ -2,18 +2,19 @@ package controller import ( "context" + "log" + "os" + "testing" + "github.com/go-logr/stdr" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - "log" - "os" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" logf "sigs.k8s.io/controller-runtime/pkg/log" - "testing" ) var ( @@ -46,6 +47,7 @@ var _ = BeforeSuite(func() { scheme := runtime.NewScheme() err = clientgoscheme.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) kubeClient, err = client.New(cfg, client.Options{Scheme: scheme})