From f669a969cc40e353161510689a77ea7c8c425eb4 Mon Sep 17 00:00:00 2001 From: Contre Date: Wed, 4 Feb 2026 17:24:38 +0100 Subject: [PATCH 01/26] feat(access-control): Add support for Kubernetes Label --- README.md | 2 +- go.mod | 27 +++ go.sum | 84 +++++++ internal/bootstrap/service_bootstrap.go | 34 ++- internal/config/config.go | 1 + internal/service/access_controls_service.go | 16 +- internal/service/kubernetes_service.go | 230 ++++++++++++++++++++ internal/service/label_provider.go | 7 + 8 files changed, 383 insertions(+), 18 deletions(-) create mode 100644 internal/service/kubernetes_service.go create mode 100644 internal/service/label_provider.go diff --git a/README.md b/README.md index 81dd2c37..c1a1bdda 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@
-Tinyauth is a simple authentication middleware that adds a simple login screen or OAuth with Google, Github or any other provider to all of your apps. It supports all the popular proxies like Traefik, Nginx and Caddy. +Tinyauth is a simple authentication middleware that adds a simple login screen or OAuth with Google, Github or any other provider to all of your apps. It supports all the popular proxies like Traefik, Nginx and Caddy, and can read configuration from Docker container labels or Kubernetes Ingress annotations. ![Screenshot](assets/screenshot.png) diff --git a/go.mod b/go.mod index 16d1362f..b77b0515 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,8 @@ require ( golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 golang.org/x/oauth2 v0.34.0 gotest.tools/v3 v3.5.2 + k8s.io/apimachinery v0.32.2 + k8s.io/client-go v0.32.2 modernc.org/sqlite v1.44.3 ) @@ -53,29 +55,41 @@ require ( github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.28.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/goccy/go-yaml v1.18.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/imdario/mergo v0.3.11 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -92,6 +106,7 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect @@ -103,8 +118,10 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect @@ -118,10 +135,20 @@ require ( golang.org/x/sys v0.40.0 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/text v0.33.0 // indirect + golang.org/x/time v0.12.0 // indirect google.golang.org/protobuf v1.36.9 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.32.2 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect modernc.org/libc v1.67.6 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect rsc.io/qr v0.2.0 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 20c692a3..4e5dda82 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,7 @@ github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmC github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -89,12 +90,16 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= @@ -112,6 +117,14 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/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-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +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.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.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -120,19 +133,30 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= +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.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -161,12 +185,19 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 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/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -175,6 +206,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -219,8 +252,15 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +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/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -256,6 +296,8 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +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= @@ -273,8 +315,12 @@ github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/weppos/publicsuffix-go v0.50.2 h1:KsJFc8IEKTJovM46SRCnGNsM+rFShxcs6VEHjOJcXzE= github.com/weppos/publicsuffix-go v0.50.2/go.mod h1:CbQCKDtXF8UcT7hrxeMa0MDjwhpOI9iYOU7cfq+yo8k= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= @@ -301,16 +347,23 @@ go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= +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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +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= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= @@ -319,10 +372,14 @@ golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -349,10 +406,15 @@ golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= @@ -365,6 +427,10 @@ google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXn gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -373,6 +439,18 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= @@ -403,3 +481,9 @@ modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +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/internal/bootstrap/service_bootstrap.go b/internal/bootstrap/service_bootstrap.go index e9a27a7f..549d6fac 100644 --- a/internal/bootstrap/service_bootstrap.go +++ b/internal/bootstrap/service_bootstrap.go @@ -10,6 +10,7 @@ type Services struct { accessControlService *service.AccessControlsService authService *service.AuthService dockerService *service.DockerService + kubernetesService *service.KubernetesService ldapService *service.LdapService oauthBrokerService *service.OAuthBrokerService oidcService *service.OIDCService @@ -38,17 +39,32 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er services.ldapService = ldapService - dockerService := service.NewDockerService() - - err = dockerService.Init() - - if err != nil { - return Services{}, err + // Determine label provider + var labelProvider service.LabelProvider + var dockerService *service.DockerService + var kubernetesService *service.KubernetesService + + switch app.config.LabelProvider { + case "kubernetes": + kubernetesService = service.NewKubernetesService() + err = kubernetesService.Init() + if err != nil { + return Services{}, err + } + services.kubernetesService = kubernetesService + labelProvider = kubernetesService + // DockerService not created when using Kubernetes + default: + dockerService = service.NewDockerService() + err = dockerService.Init() + if err != nil { + return Services{}, err + } + services.dockerService = dockerService + labelProvider = dockerService } - services.dockerService = dockerService - - accessControlsService := service.NewAccessControlsService(dockerService, app.config.Apps) + accessControlsService := service.NewAccessControlsService(labelProvider, app.config.Apps) err = accessControlsService.Init() diff --git a/internal/config/config.go b/internal/config/config.go index 700e95c3..5f280454 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -28,6 +28,7 @@ type Config struct { OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"` UI UIConfig `description:"UI customization." yaml:"ui"` Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"` + LabelProvider string `description:"Label provider to use for ACLs (docker or kubernetes)." yaml:"labelProvider"` Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"` Log LogConfig `description:"Logging configuration." yaml:"log"` } diff --git a/internal/service/access_controls_service.go b/internal/service/access_controls_service.go index 087ce883..c9accda6 100644 --- a/internal/service/access_controls_service.go +++ b/internal/service/access_controls_service.go @@ -9,14 +9,14 @@ import ( ) type AccessControlsService struct { - docker *DockerService - static map[string]config.App + labelProvider LabelProvider + static map[string]config.App } -func NewAccessControlsService(docker *DockerService, static map[string]config.App) *AccessControlsService { +func NewAccessControlsService(labelProvider LabelProvider, static map[string]config.App) *AccessControlsService { return &AccessControlsService{ - docker: docker, - static: static, + labelProvider: labelProvider, + static: static, } } @@ -48,7 +48,7 @@ func (acls *AccessControlsService) GetAccessControls(domain string) (config.App, return app, nil } - // Fallback to Docker labels - tlog.App.Debug().Msg("Falling back to Docker labels for ACLs") - return acls.docker.GetLabels(domain) + // Fallback to label provider + tlog.App.Debug().Msg("Falling back to label provider for ACLs") + return acls.labelProvider.GetLabels(domain) } diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go new file mode 100644 index 00000000..855b79d8 --- /dev/null +++ b/internal/service/kubernetes_service.go @@ -0,0 +1,230 @@ +package service + +import ( + "context" + "strings" + "sync" + "time" + + "github.com/steveiliop56/tinyauth/internal/config" + "github.com/steveiliop56/tinyauth/internal/utils/decoders" + "github.com/steveiliop56/tinyauth/internal/utils/tlog" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +var _ = unstructured.Unstructured{} + +type KubernetesService struct { + client dynamic.Interface + ctx context.Context + cancel context.CancelFunc + started bool + v1GVR *schema.GroupVersionResource + v1beta1GVR *schema.GroupVersionResource + mu sync.RWMutex + cache map[string]config.App +} + +func NewKubernetesService() *KubernetesService { + return &KubernetesService{ + cache: make(map[string]config.App), + } +} + +func (k *KubernetesService) Init() error { + var config *rest.Config + var err error + + // Try in-cluster config first + config, err = rest.InClusterConfig() + if err != nil { + // Fall back to kubeconfig + kubeconfig := clientcmd.NewDefaultClientConfigLoadingRules().GetDefaultFilename() + config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + tlog.App.Debug().Err(err).Msg("Kubernetes not connected") + k.started = false + return nil + } + } + + client, err := dynamic.NewForConfig(config) + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to create Kubernetes client") + k.started = false + return nil + } + + // Create discovery client to check available APIs + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to create discovery client") + k.started = false + return nil + } + + k.client = client + k.ctx, k.cancel = context.WithCancel(context.Background()) + + // Check which Ingress APIs are available + apiGroups, err := discoveryClient.ServerPreferredResources() + if err != nil { + // This can happen with partial discovery errors, log and continue + tlog.App.Debug().Err(err).Msg("Failed to discover API resources") + } + + // Start watching available Ingress APIs + v1Available := false + v1beta1Available := false + + if apiGroups != nil { + for _, apiGroup := range apiGroups { + if apiGroup.GroupVersion == "networking.k8s.io/v1" { + for _, resource := range apiGroup.APIResources { + if resource.Name == "ingresses" && resource.Kind == "Ingress" { + v1Available = true + } + } + } + if apiGroup.GroupVersion == "extensions/v1beta1" { + for _, resource := range apiGroup.APIResources { + if resource.Name == "ingresses" && resource.Kind == "Ingress" { + v1beta1Available = true + } + } + } + } + } + + // Check permissions for available APIs + checkAccess := func(gvr schema.GroupVersionResource) bool { + ctx, cancel := context.WithTimeout(k.ctx, 5*time.Second) + defer cancel() + + _, err := k.client.Resource(gvr).List(ctx, metav1.ListOptions{Limit: 1}) + if err != nil { + tlog.App.Debug().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Cannot access Ingress API") + return false + } + return true + } + + if v1Available { + gvr := schema.GroupVersionResource{ + Group: "networking.k8s.io", + Version: "v1", + Resource: "ingresses", + } + if checkAccess(gvr) { + tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API accessible") + k.v1GVR = &gvr + // go k.watchIngressV1() // TODO: implement watcher + } else { + tlog.App.Warn().Msg("Insufficient permissions for networking.k8s.io/v1 Ingress") + v1Available = false + } + } else { + tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API not available") + } + + if v1beta1Available { + gvr := schema.GroupVersionResource{ + Group: "extensions", + Version: "v1beta1", + Resource: "ingresses", + } + if checkAccess(gvr) { + tlog.App.Debug().Msg("extensions/v1beta1 Ingress API accessible") + k.v1beta1GVR = &gvr + // go k.watchIngressV1beta1() // TODO: implement watcher + } else { + tlog.App.Warn().Msg("Insufficient permissions for extensions/v1beta1 Ingress") + v1beta1Available = false + } + } else { + tlog.App.Debug().Msg("extensions/v1beta1 Ingress API not available") + } + + if !v1Available && !v1beta1Available { + tlog.App.Warn().Msg("No Ingress API available or accessible, Kubernetes label provider will not work") + k.started = false + return nil + } + + k.started = true + tlog.App.Info().Msg("Kubernetes label provider initialized") + return nil +} + +func (k *KubernetesService) GetLabels(appDomain string) (config.App, error) { + if !k.started { + tlog.App.Debug().Msg("Kubernetes not connected, returning empty labels") + return config.App{}, nil + } + + // Try v1 API first + if k.v1GVR != nil { + app, err := k.getLabelsFromGVR(*k.v1GVR, appDomain) + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to get labels from v1 Ingress") + } + if app.Config.Domain != "" { + return app, nil + } + } + // Fall back to v1beta1 + if k.v1beta1GVR != nil { + app, err := k.getLabelsFromGVR(*k.v1beta1GVR, appDomain) + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to get labels from v1beta1 Ingress") + } + if app.Config.Domain != "" { + return app, nil + } + } + return config.App{}, nil +} + +func (k *KubernetesService) getLabelsFromGVR(gvr schema.GroupVersionResource, appDomain string) (config.App, error) { + ctx, cancel := context.WithTimeout(k.ctx, 10*time.Second) + defer cancel() + + list, err := k.client.Resource(gvr).List(ctx, metav1.ListOptions{}) + if err != nil { + return config.App{}, err + } + + for _, item := range list.Items { + annotations := item.GetAnnotations() + if annotations == nil { + continue + } + labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") + continue + } + for appName, appLabels := range labels.Apps { + if appLabels.Config.Domain == appDomain { + tlog.App.Debug().Str("name", item.GetName()).Str("namespace", item.GetNamespace()).Msg("Found matching ingress by domain") + return appLabels, nil + } + if strings.SplitN(appDomain, ".", 2)[0] == appName { + tlog.App.Debug().Str("name", item.GetName()).Str("namespace", item.GetNamespace()).Msg("Found matching ingress by app name") + return appLabels, nil + } + } + } + return config.App{}, nil +} + +// watchIngressV1 and watchIngressV1beta1 are placeholders for future watcher implementation +// func (k *KubernetesService) watchIngressV1() {} +// func (k *KubernetesService) watchIngressV1beta1() {} diff --git a/internal/service/label_provider.go b/internal/service/label_provider.go new file mode 100644 index 00000000..9dc01bc9 --- /dev/null +++ b/internal/service/label_provider.go @@ -0,0 +1,7 @@ +package service + +import "github.com/steveiliop56/tinyauth/internal/config" + +type LabelProvider interface { + GetLabels(appDomain string) (config.App, error) +} From d2f9a8433fdbbc070b753152d6130299857d5dfa Mon Sep 17 00:00:00 2001 From: Contre Date: Thu, 5 Feb 2026 09:53:32 +0100 Subject: [PATCH 02/26] feat(access-control): Defaults to Docker --- internal/bootstrap/service_bootstrap.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/bootstrap/service_bootstrap.go b/internal/bootstrap/service_bootstrap.go index 549d6fac..850ff204 100644 --- a/internal/bootstrap/service_bootstrap.go +++ b/internal/bootstrap/service_bootstrap.go @@ -39,7 +39,6 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er services.ldapService = ldapService - // Determine label provider var labelProvider service.LabelProvider var dockerService *service.DockerService var kubernetesService *service.KubernetesService @@ -53,7 +52,6 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er } services.kubernetesService = kubernetesService labelProvider = kubernetesService - // DockerService not created when using Kubernetes default: dockerService = service.NewDockerService() err = dockerService.Init() From f1a46e297432d6d275bb8afe6f1ccb8669dc8625 Mon Sep 17 00:00:00 2001 From: Contre Date: Thu, 5 Feb 2026 12:28:11 +0100 Subject: [PATCH 03/26] feat(access-control): Remove kubeconfig fallback --- go.mod | 2 -- internal/service/kubernetes_service.go | 16 +++++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index b77b0515..c7d2790b 100644 --- a/go.mod +++ b/go.mod @@ -118,7 +118,6 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -137,7 +136,6 @@ require ( golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.12.0 // indirect google.golang.org/protobuf v1.36.9 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.32.2 // indirect diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 855b79d8..1a9ef609 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -16,7 +16,6 @@ import ( "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" ) var _ = unstructured.Unstructured{} @@ -45,19 +44,14 @@ func (k *KubernetesService) Init() error { // Try in-cluster config first config, err = rest.InClusterConfig() if err != nil { - // Fall back to kubeconfig - kubeconfig := clientcmd.NewDefaultClientConfigLoadingRules().GetDefaultFilename() - config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) - if err != nil { - tlog.App.Debug().Err(err).Msg("Kubernetes not connected") - k.started = false - return nil - } + tlog.App.Error().Err(err).Msg("Failed to get in-cluster Kubernetes config") + k.started = false + return nil } client, err := dynamic.NewForConfig(config) if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to create Kubernetes client") + tlog.App.Error().Err(err).Msg("Failed to create Kubernetes client") k.started = false return nil } @@ -65,7 +59,7 @@ func (k *KubernetesService) Init() error { // Create discovery client to check available APIs discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to create discovery client") + tlog.App.Error().Err(err).Msg("Failed to create discovery client") k.started = false return nil } From 42f10a5e274bcc6865a2a559f396ec645af90484 Mon Sep 17 00:00:00 2001 From: Contre Date: Thu, 19 Feb 2026 14:47:23 +0100 Subject: [PATCH 04/26] feat(watcher): Watcher for kubernetes service --- internal/service/kubernetes_service.go | 294 +++++++++++++++++++++++-- 1 file changed, 280 insertions(+), 14 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 1a9ef609..be921ffe 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -13,6 +13,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" @@ -20,20 +21,255 @@ import ( var _ = unstructured.Unstructured{} +type ingressKey struct { + namespace string + name string +} + +type ingressAppKey struct { + ingressKey + appName string +} + +type ingressApp struct { + domain string + appName string + app config.App +} + type KubernetesService struct { - client dynamic.Interface - ctx context.Context - cancel context.CancelFunc - started bool - v1GVR *schema.GroupVersionResource - v1beta1GVR *schema.GroupVersionResource - mu sync.RWMutex - cache map[string]config.App + client dynamic.Interface + ctx context.Context + cancel context.CancelFunc + started bool + v1GVR *schema.GroupVersionResource + v1beta1GVR *schema.GroupVersionResource + mu sync.RWMutex + ingressApps map[ingressKey][]ingressApp + domainIndex map[string]ingressAppKey + appNameIndex map[string]ingressAppKey } func NewKubernetesService() *KubernetesService { return &KubernetesService{ - cache: make(map[string]config.App), + ingressApps: make(map[ingressKey][]ingressApp), + domainIndex: make(map[string]ingressAppKey), + appNameIndex: make(map[string]ingressAppKey), + } +} + +func (k *KubernetesService) addIngressApps(namespace, name string, apps []ingressApp) { + k.mu.Lock() + defer k.mu.Unlock() + + key := ingressKey{namespace, name} + // Remove existing entries for this ingress + if existing, ok := k.ingressApps[key]; ok { + for _, app := range existing { + delete(k.domainIndex, app.domain) + delete(k.appNameIndex, app.appName) + } + } + // Add new entries + k.ingressApps[key] = apps + for _, app := range apps { + appKey := ingressAppKey{key, app.appName} + k.domainIndex[app.domain] = appKey + k.appNameIndex[app.appName] = appKey + } +} + +func (k *KubernetesService) removeIngress(namespace, name string) { + k.mu.Lock() + defer k.mu.Unlock() + + key := ingressKey{namespace, name} + if apps, ok := k.ingressApps[key]; ok { + for _, app := range apps { + delete(k.domainIndex, app.domain) + delete(k.appNameIndex, app.appName) + } + delete(k.ingressApps, key) + } +} + +func (k *KubernetesService) getByDomain(domain string) (config.App, bool) { + k.mu.RLock() + defer k.mu.RUnlock() + + if appKey, ok := k.domainIndex[domain]; ok { + if apps, ok := k.ingressApps[appKey.ingressKey]; ok { + for _, app := range apps { + if app.domain == domain && app.appName == appKey.appName { + return app.app, true + } + } + } + } + return config.App{}, false +} + +func (k *KubernetesService) getByAppName(appName string) (config.App, bool) { + k.mu.RLock() + defer k.mu.RUnlock() + + if appKey, ok := k.appNameIndex[appName]; ok { + if apps, ok := k.ingressApps[appKey.ingressKey]; ok { + for _, app := range apps { + if app.appName == appName { + return app.app, true + } + } + } + } + return config.App{}, false +} + +func (k *KubernetesService) updateFromList(items []unstructured.Unstructured) { + for _, item := range items { + namespace := item.GetNamespace() + name := item.GetName() + annotations := item.GetAnnotations() + if annotations == nil { + k.removeIngress(namespace, name) + continue + } + labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") + k.removeIngress(namespace, name) + continue + } + var apps []ingressApp + for appName, appLabels := range labels.Apps { + if appLabels.Config.Domain == "" { + continue + } + apps = append(apps, ingressApp{ + domain: appLabels.Config.Domain, + appName: appName, + app: appLabels, + }) + } + if len(apps) == 0 { + k.removeIngress(namespace, name) + } else { + k.addIngressApps(namespace, name, apps) + } + } +} + +func (k *KubernetesService) resyncGVR(gvr schema.GroupVersionResource) error { + ctx, cancel := context.WithTimeout(k.ctx, 30*time.Second) + defer cancel() + + list, err := k.client.Resource(gvr).List(ctx, metav1.ListOptions{}) + if err != nil { + tlog.App.Debug().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Failed to list ingresses during resync") + return err + } + k.updateFromList(list.Items) + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Int("count", len(list.Items)).Msg("Resynced ingress cache") + return nil +} + +func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { + resyncTicker := time.NewTicker(5 * time.Minute) + defer resyncTicker.Stop() + + // Initial resync + if err := k.resyncGVR(gvr); err != nil { + tlog.App.Error().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Initial resync failed, retrying in 30 seconds") + time.Sleep(30 * time.Second) + } + + for { + select { + case <-k.ctx.Done(): + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Stopping watcher") + return + case <-resyncTicker.C: + if err := k.resyncGVR(gvr); err != nil { + tlog.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Periodic resync failed") + } + default: + ctx, cancel := context.WithCancel(k.ctx) + watcher, err := k.client.Resource(gvr).Watch(ctx, metav1.ListOptions{}) + if err != nil { + tlog.App.Error().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Failed to start watcher") + cancel() + time.Sleep(10 * time.Second) + continue + } + + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher started") + inner: + for { + select { + case <-k.ctx.Done(): + watcher.Stop() + cancel() + return + case event, ok := <-watcher.ResultChan(): + if !ok { + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher channel closed") + watcher.Stop() + cancel() + break inner + } + switch event.Type { + case watch.Added, watch.Modified: + item, ok := event.Object.(*unstructured.Unstructured) + if !ok { + tlog.App.Warn().Str("api", gvr.GroupVersion().String()).Msg("Failed to cast watched object") + continue + } + namespace := item.GetNamespace() + name := item.GetName() + annotations := item.GetAnnotations() + if annotations == nil { + k.removeIngress(namespace, name) + continue + } + labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") + k.removeIngress(namespace, name) + continue + } + var apps []ingressApp + for appName, appLabels := range labels.Apps { + if appLabels.Config.Domain == "" { + continue + } + apps = append(apps, ingressApp{ + domain: appLabels.Config.Domain, + appName: appName, + app: appLabels, + }) + } + if len(apps) == 0 { + k.removeIngress(namespace, name) + } else { + k.addIngressApps(namespace, name, apps) + } + case watch.Deleted: + item, ok := event.Object.(*unstructured.Unstructured) + if !ok { + tlog.App.Warn().Str("api", gvr.GroupVersion().String()).Msg("Failed to cast watched object") + continue + } + k.removeIngress(item.GetNamespace(), item.GetName()) + default: + // ignore other event types + } + case <-resyncTicker.C: + if err := k.resyncGVR(gvr); err != nil { + tlog.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Periodic resync failed") + } + } + } + } } } @@ -119,7 +355,7 @@ func (k *KubernetesService) Init() error { if checkAccess(gvr) { tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API accessible") k.v1GVR = &gvr - // go k.watchIngressV1() // TODO: implement watcher + go k.watchIngressV1() } else { tlog.App.Warn().Msg("Insufficient permissions for networking.k8s.io/v1 Ingress") v1Available = false @@ -137,7 +373,7 @@ func (k *KubernetesService) Init() error { if checkAccess(gvr) { tlog.App.Debug().Msg("extensions/v1beta1 Ingress API accessible") k.v1beta1GVR = &gvr - // go k.watchIngressV1beta1() // TODO: implement watcher + go k.watchIngressV1beta1() } else { tlog.App.Warn().Msg("Insufficient permissions for extensions/v1beta1 Ingress") v1beta1Available = false @@ -163,6 +399,19 @@ func (k *KubernetesService) GetLabels(appDomain string) (config.App, error) { return config.App{}, nil } + // First check cache + if app, found := k.getByDomain(appDomain); found { + tlog.App.Debug().Str("domain", appDomain).Msg("Found labels in cache by domain") + return app, nil + } + appName := strings.SplitN(appDomain, ".", 2)[0] + if app, found := k.getByAppName(appName); found { + tlog.App.Debug().Str("domain", appDomain).Str("appName", appName).Msg("Found labels in cache by app name") + return app, nil + } + + tlog.App.Debug().Str("domain", appDomain).Msg("Cache miss, falling back to API") + // Try v1 API first if k.v1GVR != nil { app, err := k.getLabelsFromGVR(*k.v1GVR, appDomain) @@ -195,6 +444,11 @@ func (k *KubernetesService) getLabelsFromGVR(gvr schema.GroupVersionResource, ap return config.App{}, err } + // Update cache with all ingresses from this list + k.updateFromList(list.Items) + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Int("count", len(list.Items)).Msg("Updated cache from list") + + // Search for matching app for _, item := range list.Items { annotations := item.GetAnnotations() if annotations == nil { @@ -219,6 +473,18 @@ func (k *KubernetesService) getLabelsFromGVR(gvr schema.GroupVersionResource, ap return config.App{}, nil } -// watchIngressV1 and watchIngressV1beta1 are placeholders for future watcher implementation -// func (k *KubernetesService) watchIngressV1() {} -// func (k *KubernetesService) watchIngressV1beta1() {} +// watchIngressV1 starts watching networking.k8s.io/v1 ingresses +func (k *KubernetesService) watchIngressV1() { + if k.v1GVR == nil { + return + } + k.watchGVR(*k.v1GVR) +} + +// watchIngressV1beta1 starts watching extensions/v1beta1 ingresses +func (k *KubernetesService) watchIngressV1beta1() { + if k.v1beta1GVR == nil { + return + } + k.watchGVR(*k.v1beta1GVR) +} From cf82d66e40b670e26aa5ecbb961e5b0f860d6e4d Mon Sep 17 00:00:00 2001 From: Contre Date: Fri, 20 Feb 2026 13:04:50 +0100 Subject: [PATCH 05/26] feat(watcher): Merge with main + remove nightly fix redirect --- internal/config/config.go | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 7711a2b9..88f19d97 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -68,22 +68,21 @@ var RedirectCookieName = "tinyauth-redirect" // Main app config type Config struct { - AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` - ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"` - DatabasePath string `description:"The path to the database file." yaml:"databasePath"` - DisableAnalytics bool `description:"Disable analytics." yaml:"disableAnalytics"` - DisableResources bool `description:"Disable resources server." yaml:"disableResources"` - DisableUIWarnings bool `description:"Disable UI warnings." yaml:"disableUIWarnings"` - Server ServerConfig `description:"Server configuration." yaml:"server"` - Auth AuthConfig `description:"Authentication configuration." yaml:"auth"` - Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"` - OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"` - OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"` - UI UIConfig `description:"UI customization." yaml:"ui"` - Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"` - LabelProvider string `description:"Label provider to use for ACLs (docker or kubernetes)." yaml:"labelProvider"` - Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"` - Log LogConfig `description:"Logging configuration." yaml:"log"` + AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"` + ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"` + DatabasePath string `description:"The path to the database file." yaml:"databasePath"` + DisableAnalytics bool `description:"Disable analytics." yaml:"disableAnalytics"` + DisableResources bool `description:"Disable resources server." yaml:"disableResources"` + Server ServerConfig `description:"Server configuration." yaml:"server"` + Auth AuthConfig `description:"Authentication configuration." yaml:"auth"` + Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"` + OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"` + OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"` + UI UIConfig `description:"UI customization." yaml:"ui"` + Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"` + LabelProvider string `description:"Label provider to use for ACLs (docker or kubernetes)." yaml:"labelProvider"` + Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"` + Log LogConfig `description:"Logging configuration." yaml:"log"` } type ServerConfig struct { From 858519beb0144943a627d956b2b1d6f2395821c4 Mon Sep 17 00:00:00 2001 From: Contre Date: Mon, 16 Mar 2026 14:25:25 +0100 Subject: [PATCH 06/26] fix(go): Go mod + Go sum after sync with main --- go.mod | 13 ++++++------- go.sum | 11 ++++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 96c794e8..81274bb5 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,6 @@ require ( gotest.tools/v3 v3.5.2 k8s.io/apimachinery v0.32.2 k8s.io/client-go v0.32.2 - modernc.org/sqlite v1.45.0 modernc.org/sqlite v1.46.1 ) @@ -74,12 +73,12 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect @@ -119,8 +118,8 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/x448/float16 v0.8.4 // indirect github.com/ugorji/go/codec v1.3.1 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -129,15 +128,15 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect - golang.org/x/time v0.12.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect golang.org/x/arch v0.22.0 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/term v0.41.0 // indirect golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.12.0 // indirect google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.32.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/go.sum b/go.sum index 1aa6c336..c7d55069 100644 --- a/go.sum +++ b/go.sum @@ -133,10 +133,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -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/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +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.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= @@ -309,18 +309,19 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/weppos/publicsuffix-go v0.50.3 h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE= github.com/weppos/publicsuffix-go v0.50.3/go.mod h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -364,9 +365,9 @@ golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +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= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= From d5d733f096104705cccd08f10b57934bd184fc9e Mon Sep 17 00:00:00 2001 From: Contre Date: Mon, 16 Mar 2026 14:26:15 +0100 Subject: [PATCH 07/26] fix(config): Ser default value for LabelProvider to Docker --- internal/config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/config/config.go b/internal/config/config.go index b7c2b3b1..05adb6c5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -59,6 +59,7 @@ func NewDefaultConfiguration() *Config { Experimental: ExperimentalConfig{ ConfigFile: "", }, + LabelProvider: "docker", } } From 010e216d82e1ab01d39dd9e8e0955c862460797a Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 16:20:57 +0200 Subject: [PATCH 08/26] feat(go): go mod tidy --- go.mod | 17 +++++++---------- go.sum | 60 ++++++++++++---------------------------------------------- 2 files changed, 19 insertions(+), 58 deletions(-) diff --git a/go.mod b/go.mod index 2813efd3..e86c5f44 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.26.0 require ( charm.land/huh/v2 v2.0.3 - github.com/cenkalti/backoff/v5 v5.0.3SECRETS_FILE + github.com/cenkalti/backoff/v5 v5.0.3 github.com/docker/docker v28.5.2+incompatible github.com/gin-gonic/gin v1.12.0 github.com/go-jose/go-jose/v4 v4.1.4 @@ -65,7 +65,6 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect @@ -87,13 +86,12 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/imdario/mergo v0.3.11 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.20 // indirect @@ -107,7 +105,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -137,17 +134,17 @@ require ( golang.org/x/arch v0.22.0 // indirect golang.org/x/net v0.52.0 // indirect golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/term v0.42.0 // indirect + golang.org/x/text v0.36.0 // indirect golang.org/x/time v0.12.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.32.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.70.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect diff --git a/go.sum b/go.sum index 442f9c53..16daaa99 100644 --- a/go.sum +++ b/go.sum @@ -81,7 +81,6 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= @@ -101,8 +100,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -148,7 +145,6 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= @@ -209,11 +205,10 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= +github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= -github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -247,8 +242,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 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/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= @@ -317,7 +310,6 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -347,61 +339,37 @@ golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= 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= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= @@ -410,13 +378,12 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= @@ -433,9 +400,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= From b2ed7ed9d9ed1d72773435d17c255acb48d55c9b Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:34:24 +0200 Subject: [PATCH 09/26] feat(k8s_service): Remove logic for deprecated Ingress k8s v1.22 --- internal/service/kubernetes_service.go | 45 +++++++------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index be921ffe..60aff21a 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -43,7 +43,6 @@ type KubernetesService struct { cancel context.CancelFunc started bool v1GVR *schema.GroupVersionResource - v1beta1GVR *schema.GroupVersionResource mu sync.RWMutex ingressApps map[ingressKey][]ingressApp domainIndex map[string]ingressAppKey @@ -274,30 +273,22 @@ func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { } func (k *KubernetesService) Init() error { - var config *rest.Config + var cfg *rest.Config var err error - // Try in-cluster config first - config, err = rest.InClusterConfig() + cfg, err = rest.InClusterConfig() if err != nil { - tlog.App.Error().Err(err).Msg("Failed to get in-cluster Kubernetes config") - k.started = false - return nil + return fmt.Errorf("failed to get in-cluster Kubernetes config: %w", err) } - client, err := dynamic.NewForConfig(config) + client, err := dynamic.NewForConfig(cfg) if err != nil { - tlog.App.Error().Err(err).Msg("Failed to create Kubernetes client") - k.started = false - return nil + return fmt.Errorf("failed to create Kubernetes client: %w", err) } - // Create discovery client to check available APIs - discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg) if err != nil { - tlog.App.Error().Err(err).Msg("Failed to create discovery client") - k.started = false - return nil + return fmt.Errorf("failed to create discovery client: %w", err) } k.client = client @@ -310,24 +301,12 @@ func (k *KubernetesService) Init() error { tlog.App.Debug().Err(err).Msg("Failed to discover API resources") } - // Start watching available Ingress APIs v1Available := false - v1beta1Available := false - - if apiGroups != nil { - for _, apiGroup := range apiGroups { - if apiGroup.GroupVersion == "networking.k8s.io/v1" { - for _, resource := range apiGroup.APIResources { - if resource.Name == "ingresses" && resource.Kind == "Ingress" { - v1Available = true - } - } - } - if apiGroup.GroupVersion == "extensions/v1beta1" { - for _, resource := range apiGroup.APIResources { - if resource.Name == "ingresses" && resource.Kind == "Ingress" { - v1beta1Available = true - } + for _, apiGroup := range apiGroups { + if apiGroup.GroupVersion == "networking.k8s.io/v1" { + for _, resource := range apiGroup.APIResources { + if resource.Name == "ingresses" && resource.Kind == "Ingress" { + v1Available = true } } } From b4c8a8176294c16dcd42a8f35e0e6f1144748479 Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:35:27 +0200 Subject: [PATCH 10/26] feat(k8s_service): (Watcher) -> Wait 5s before breaking to outer loop again --- internal/service/kubernetes_service.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 60aff21a..fd4f0096 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -211,9 +211,10 @@ func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { return case event, ok := <-watcher.ResultChan(): if !ok { - tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher channel closed") + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher channel closed, restarting in 5 seconds") watcher.Stop() cancel() + time.Sleep(5 * time.Second) break inner } switch event.Type { From 2796f9b734f1a062d9d10b32cdc24769796653e5 Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:35:41 +0200 Subject: [PATCH 11/26] feat(k8s_service): Remove logic for deprecated Ingress k8s v1.22 --- internal/service/kubernetes_service.go | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index fd4f0096..13cb5bac 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -344,25 +344,7 @@ func (k *KubernetesService) Init() error { tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API not available") } - if v1beta1Available { - gvr := schema.GroupVersionResource{ - Group: "extensions", - Version: "v1beta1", - Resource: "ingresses", - } - if checkAccess(gvr) { - tlog.App.Debug().Msg("extensions/v1beta1 Ingress API accessible") - k.v1beta1GVR = &gvr - go k.watchIngressV1beta1() - } else { - tlog.App.Warn().Msg("Insufficient permissions for extensions/v1beta1 Ingress") - v1beta1Available = false - } - } else { - tlog.App.Debug().Msg("extensions/v1beta1 Ingress API not available") - } - - if !v1Available && !v1beta1Available { + if !v1Available { tlog.App.Warn().Msg("No Ingress API available or accessible, Kubernetes label provider will not work") k.started = false return nil From 8ee46aa8a49ce3604258b7f33e3e7a20e0e3141f Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:35:57 +0200 Subject: [PATCH 12/26] feat(k8s_service): Remove logic for deprecated Ingress k8s v1.22 --- internal/service/kubernetes_service.go | 61 +------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 13cb5bac..dc81d9db 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -372,66 +372,7 @@ func (k *KubernetesService) GetLabels(appDomain string) (config.App, error) { return app, nil } - tlog.App.Debug().Str("domain", appDomain).Msg("Cache miss, falling back to API") - - // Try v1 API first - if k.v1GVR != nil { - app, err := k.getLabelsFromGVR(*k.v1GVR, appDomain) - if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to get labels from v1 Ingress") - } - if app.Config.Domain != "" { - return app, nil - } - } - // Fall back to v1beta1 - if k.v1beta1GVR != nil { - app, err := k.getLabelsFromGVR(*k.v1beta1GVR, appDomain) - if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to get labels from v1beta1 Ingress") - } - if app.Config.Domain != "" { - return app, nil - } - } - return config.App{}, nil -} - -func (k *KubernetesService) getLabelsFromGVR(gvr schema.GroupVersionResource, appDomain string) (config.App, error) { - ctx, cancel := context.WithTimeout(k.ctx, 10*time.Second) - defer cancel() - - list, err := k.client.Resource(gvr).List(ctx, metav1.ListOptions{}) - if err != nil { - return config.App{}, err - } - - // Update cache with all ingresses from this list - k.updateFromList(list.Items) - tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Int("count", len(list.Items)).Msg("Updated cache from list") - - // Search for matching app - for _, item := range list.Items { - annotations := item.GetAnnotations() - if annotations == nil { - continue - } - labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") - if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") - continue - } - for appName, appLabels := range labels.Apps { - if appLabels.Config.Domain == appDomain { - tlog.App.Debug().Str("name", item.GetName()).Str("namespace", item.GetNamespace()).Msg("Found matching ingress by domain") - return appLabels, nil - } - if strings.SplitN(appDomain, ".", 2)[0] == appName { - tlog.App.Debug().Str("name", item.GetName()).Str("namespace", item.GetNamespace()).Msg("Found matching ingress by app name") - return appLabels, nil - } - } - } + tlog.App.Debug().Str("domain", appDomain).Msg("Cache miss, no matching ingress found") return config.App{}, nil } From e4a1f7cc785c6f59a24bcd7a817b64035b2e57f6 Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:36:12 +0200 Subject: [PATCH 13/26] feat(k8s_service): Remove logic for deprecated Ingress k8s v1.22 --- internal/service/kubernetes_service.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index dc81d9db..7a2b2ee5 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -383,11 +383,3 @@ func (k *KubernetesService) watchIngressV1() { } k.watchGVR(*k.v1GVR) } - -// watchIngressV1beta1 starts watching extensions/v1beta1 ingresses -func (k *KubernetesService) watchIngressV1beta1() { - if k.v1beta1GVR == nil { - return - } - k.watchGVR(*k.v1beta1GVR) -} From b66c7fdf54660adadee2d93eebe4c5082de7a884 Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:36:51 +0200 Subject: [PATCH 14/26] feat(k8s_service): Remove var _ = unstructured.Unstructured{} + comments + msg edits --- internal/service/kubernetes_service.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 7a2b2ee5..6d2fd08f 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -2,6 +2,7 @@ package service import ( "context" + "fmt" "strings" "sync" "time" @@ -19,8 +20,6 @@ import ( "k8s.io/client-go/rest" ) -var _ = unstructured.Unstructured{} - type ingressKey struct { namespace string name string @@ -200,7 +199,6 @@ func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { time.Sleep(10 * time.Second) continue } - tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher started") inner: for { @@ -313,7 +311,7 @@ func (k *KubernetesService) Init() error { } } - // Check permissions for available APIs + // Check permissions for the v1 API checkAccess := func(gvr schema.GroupVersionResource) bool { ctx, cancel := context.WithTimeout(k.ctx, 5*time.Second) defer cancel() From 1ff00004ffb0bee59d4e73a67c2fec2b3a3d7873 Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:37:31 +0200 Subject: [PATCH 15/26] feat(bootstrap): Remove dockerService from bootstrap svc --- internal/bootstrap/service_bootstrap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/bootstrap/service_bootstrap.go b/internal/bootstrap/service_bootstrap.go index b34b4fde..43e8d878 100644 --- a/internal/bootstrap/service_bootstrap.go +++ b/internal/bootstrap/service_bootstrap.go @@ -94,7 +94,7 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er SessionCookieName: app.context.sessionCookieName, IP: app.config.Auth.IP, LDAPGroupsCacheTTL: app.config.Ldap.GroupCacheTTL, - }, dockerService, services.ldapService, queries, services.oauthBrokerService) + }, services.ldapService, queries, services.oauthBrokerService) err = authService.Init() From 406c7dd3bcfba895a2b7615be28f2c0e60c3fb0b Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:37:58 +0200 Subject: [PATCH 16/26] feat(auth_svc): Remove dockerService from authservice --- internal/service/auth_service.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/service/auth_service.go b/internal/service/auth_service.go index 807d39c5..72429f0a 100644 --- a/internal/service/auth_service.go +++ b/internal/service/auth_service.go @@ -82,7 +82,6 @@ type AuthServiceConfig struct { type AuthService struct { config AuthServiceConfig - docker *DockerService loginAttempts map[string]*LoginAttempt ldapGroupsCache map[string]*LdapGroupsCache oauthPendingSessions map[string]*OAuthPendingSession @@ -97,17 +96,16 @@ type AuthService struct { lockdownCancelFunc context.CancelFunc } -func NewAuthService(config AuthServiceConfig, docker *DockerService, ldap *LdapService, queries *repository.Queries, oauthBroker *OAuthBrokerService) *AuthService { +func NewAuthService(config AuthServiceConfig, ldap *LdapService, queries *repository.Queries, oauthBroker *OAuthBrokerService) *AuthService { return &AuthService{ config: config, - docker: docker, loginAttempts: make(map[string]*LoginAttempt), ldapGroupsCache: make(map[string]*LdapGroupsCache), oauthPendingSessions: make(map[string]*OAuthPendingSession), ldap: ldap, queries: queries, oauthBroker: oauthBroker, - } +} } func (auth *AuthService) Init() error { From fd269f4ce4392643d2016cff5a1b0fc0dec50720 Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:38:36 +0200 Subject: [PATCH 17/26] feat(test): Add tests for kubernetes_services --- internal/service/kubernetes_service_test.go | 176 ++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 internal/service/kubernetes_service_test.go diff --git a/internal/service/kubernetes_service_test.go b/internal/service/kubernetes_service_test.go new file mode 100644 index 00000000..080c3068 --- /dev/null +++ b/internal/service/kubernetes_service_test.go @@ -0,0 +1,176 @@ +package service + +import ( + "testing" + + "github.com/steveiliop56/tinyauth/internal/config" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestKubernetesService() *KubernetesService { + return &KubernetesService{ + ingressApps: make(map[ingressKey][]ingressApp), + domainIndex: make(map[string]ingressAppKey), + appNameIndex: make(map[string]ingressAppKey), + } +} + +func TestKubernetesService_CacheByDomain(t *testing.T) { + svc := newTestKubernetesService() + + app := config.App{Config: config.AppConfig{Domain: "foo.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "foo.example.com", appName: "foo", app: app}, + }) + + got, ok := svc.getByDomain("foo.example.com") + require.True(t, ok) + assert.Equal(t, "foo.example.com", got.Config.Domain) + + _, ok = svc.getByDomain("notfound.example.com") + assert.False(t, ok) +} + +func TestKubernetesService_CacheByAppName(t *testing.T) { + svc := newTestKubernetesService() + + app := config.App{Config: config.AppConfig{Domain: "bar.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "bar.example.com", appName: "bar", app: app}, + }) + + got, ok := svc.getByAppName("bar") + require.True(t, ok) + assert.Equal(t, "bar.example.com", got.Config.Domain) + + _, ok = svc.getByAppName("notfound") + assert.False(t, ok) +} + +func TestKubernetesService_RemoveIngress(t *testing.T) { + svc := newTestKubernetesService() + + app := config.App{Config: config.AppConfig{Domain: "baz.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "baz.example.com", appName: "baz", app: app}, + }) + + svc.removeIngress("default", "my-ingress") + + _, ok := svc.getByDomain("baz.example.com") + assert.False(t, ok) + _, ok = svc.getByAppName("baz") + assert.False(t, ok) +} + +func TestKubernetesService_AddIngressApps_Replaces(t *testing.T) { + svc := newTestKubernetesService() + + old := config.App{Config: config.AppConfig{Domain: "old.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "old.example.com", appName: "old", app: old}, + }) + + updated := config.App{Config: config.AppConfig{Domain: "new.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "new.example.com", appName: "new", app: updated}, + }) + + // Old entry should be gone + _, ok := svc.getByDomain("old.example.com") + assert.False(t, ok) + + // New entry should be present + got, ok := svc.getByDomain("new.example.com") + require.True(t, ok) + assert.Equal(t, "new.example.com", got.Config.Domain) +} + +func TestKubernetesService_GetLabels_CacheHit(t *testing.T) { + svc := newTestKubernetesService() + svc.started = true + + app := config.App{Config: config.AppConfig{Domain: "hit.example.com"}} + svc.addIngressApps("default", "ing", []ingressApp{ + {domain: "hit.example.com", appName: "hit", app: app}, + }) + + got, err := svc.GetLabels("hit.example.com") + require.NoError(t, err) + assert.Equal(t, "hit.example.com", got.Config.Domain) +} + +func TestKubernetesService_GetLabels_CacheMiss(t *testing.T) { + svc := newTestKubernetesService() + svc.started = true + + got, err := svc.GetLabels("notfound.example.com") + require.NoError(t, err) + assert.Equal(t, config.App{}, got) +} + +func TestKubernetesService_GetLabels_ByAppName(t *testing.T) { + svc := newTestKubernetesService() + svc.started = true + + app := config.App{Config: config.AppConfig{Domain: "myapp.internal.example.com"}} + svc.addIngressApps("default", "ing", []ingressApp{ + {domain: "myapp.internal.example.com", appName: "myapp", app: app}, + }) + + // Look up by subdomain prefix matching appName + got, err := svc.GetLabels("myapp.internal.example.com") + require.NoError(t, err) + assert.Equal(t, "myapp.internal.example.com", got.Config.Domain) +} + +func TestKubernetesService_GetLabels_NotStarted(t *testing.T) { + svc := newTestKubernetesService() + // started is false by default + + got, err := svc.GetLabels("anything.example.com") + require.NoError(t, err) + assert.Equal(t, config.App{}, got) +} + +func TestKubernetesService_UpdateFromList(t *testing.T) { + svc := newTestKubernetesService() + + item := unstructured.Unstructured{} + item.SetNamespace("default") + item.SetName("test-ingress") + item.SetAnnotations(map[string]string{ + "tinyauth.apps.myapp.config.domain": "myapp.example.com", + "tinyauth.apps.myapp.users.allow": "alice", + }) + + svc.updateFromList([]unstructured.Unstructured{item}) + + got, ok := svc.getByDomain("myapp.example.com") + require.True(t, ok) + assert.Equal(t, "myapp.example.com", got.Config.Domain) + assert.Equal(t, "alice", got.Users.Allow) +} + +func TestKubernetesService_UpdateFromList_NoAnnotations(t *testing.T) { + svc := newTestKubernetesService() + + // First add something to the cache + app := config.App{Config: config.AppConfig{Domain: "todelete.example.com"}} + svc.addIngressApps("default", "test-ingress", []ingressApp{ + {domain: "todelete.example.com", appName: "todelete", app: app}, + }) + + // Now update with an item that has no annotations + item := unstructured.Unstructured{} + item.SetNamespace("default") + item.SetName("test-ingress") + + svc.updateFromList([]unstructured.Unstructured{item}) + + _, ok := svc.getByDomain("todelete.example.com") + assert.False(t, ok) +} From 0843aca9cb78ebf2f3ad26f7b7c0fd29c4a24e3a Mon Sep 17 00:00:00 2001 From: Contre Date: Sun, 19 Apr 2026 21:39:43 +0200 Subject: [PATCH 18/26] feat(test): Remove docker serivce form proxy/user test --- internal/controller/proxy_controller_test.go | 2 +- internal/controller/user_controller_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/controller/proxy_controller_test.go b/internal/controller/proxy_controller_test.go index 5c07acf5..64317127 100644 --- a/internal/controller/proxy_controller_test.go +++ b/internal/controller/proxy_controller_test.go @@ -412,7 +412,7 @@ func TestProxyController(t *testing.T) { err = broker.Init() require.NoError(t, err) - authService := service.NewAuthService(authServiceCfg, docker, ldap, queries, broker) + authService := service.NewAuthService(authServiceCfg, ldap, queries, broker) err = authService.Init() require.NoError(t, err) diff --git a/internal/controller/user_controller_test.go b/internal/controller/user_controller_test.go index d69f9304..7d3b8648 100644 --- a/internal/controller/user_controller_test.go +++ b/internal/controller/user_controller_test.go @@ -296,7 +296,7 @@ func TestUserController(t *testing.T) { err = broker.Init() require.NoError(t, err) - authService := service.NewAuthService(authServiceCfg, docker, ldap, queries, broker) + authService := service.NewAuthService(authServiceCfg, ldap, queries, broker) err = authService.Init() require.NoError(t, err) From 65493a883aa4da333294ab4dfc9e0fe436fc7501 Mon Sep 17 00:00:00 2001 From: Contre Date: Wed, 22 Apr 2026 12:34:47 +0200 Subject: [PATCH 19/26] fix(refactor): Remove update logic from watcher and resync --- internal/service/kubernetes_service.go | 90 +++++++-------------- internal/service/kubernetes_service_test.go | 8 +- 2 files changed, 35 insertions(+), 63 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 6d2fd08f..30e8887e 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -123,37 +123,35 @@ func (k *KubernetesService) getByAppName(appName string) (config.App, bool) { return config.App{}, false } -func (k *KubernetesService) updateFromList(items []unstructured.Unstructured) { - for _, item := range items { - namespace := item.GetNamespace() - name := item.GetName() - annotations := item.GetAnnotations() - if annotations == nil { - k.removeIngress(namespace, name) - continue - } - labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") - if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") - k.removeIngress(namespace, name) +func (k *KubernetesService) updateFromItem(item *unstructured.Unstructured) { + namespace := item.GetNamespace() + name := item.GetName() + annotations := item.GetAnnotations() + if annotations == nil { + k.removeIngress(namespace, name) + return + } + labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") + if err != nil { + tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") + k.removeIngress(namespace, name) + return + } + var apps []ingressApp + for appName, appLabels := range labels.Apps { + if appLabels.Config.Domain == "" { continue } - var apps []ingressApp - for appName, appLabels := range labels.Apps { - if appLabels.Config.Domain == "" { - continue - } - apps = append(apps, ingressApp{ - domain: appLabels.Config.Domain, - appName: appName, - app: appLabels, - }) - } - if len(apps) == 0 { - k.removeIngress(namespace, name) - } else { - k.addIngressApps(namespace, name, apps) - } + apps = append(apps, ingressApp{ + domain: appLabels.Config.Domain, + appName: appName, + app: appLabels, + }) + } + if len(apps) == 0 { + k.removeIngress(namespace, name) + } else { + k.addIngressApps(namespace, name, apps) } } @@ -166,7 +164,9 @@ func (k *KubernetesService) resyncGVR(gvr schema.GroupVersionResource) error { tlog.App.Debug().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Failed to list ingresses during resync") return err } - k.updateFromList(list.Items) + for i := range list.Items { + k.updateFromItem(&list.Items[i]) + } tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Int("count", len(list.Items)).Msg("Resynced ingress cache") return nil } @@ -222,35 +222,7 @@ func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { tlog.App.Warn().Str("api", gvr.GroupVersion().String()).Msg("Failed to cast watched object") continue } - namespace := item.GetNamespace() - name := item.GetName() - annotations := item.GetAnnotations() - if annotations == nil { - k.removeIngress(namespace, name) - continue - } - labels, err := decoders.DecodeLabels[config.Apps](annotations, "apps") - if err != nil { - tlog.App.Debug().Err(err).Msg("Failed to decode labels from annotations") - k.removeIngress(namespace, name) - continue - } - var apps []ingressApp - for appName, appLabels := range labels.Apps { - if appLabels.Config.Domain == "" { - continue - } - apps = append(apps, ingressApp{ - domain: appLabels.Config.Domain, - appName: appName, - app: appLabels, - }) - } - if len(apps) == 0 { - k.removeIngress(namespace, name) - } else { - k.addIngressApps(namespace, name, apps) - } + k.updateFromItem(item) case watch.Deleted: item, ok := event.Object.(*unstructured.Unstructured) if !ok { diff --git a/internal/service/kubernetes_service_test.go b/internal/service/kubernetes_service_test.go index 080c3068..0b374910 100644 --- a/internal/service/kubernetes_service_test.go +++ b/internal/service/kubernetes_service_test.go @@ -136,7 +136,7 @@ func TestKubernetesService_GetLabels_NotStarted(t *testing.T) { assert.Equal(t, config.App{}, got) } -func TestKubernetesService_UpdateFromList(t *testing.T) { +func TestKubernetesService_UpdateFromItem(t *testing.T) { svc := newTestKubernetesService() item := unstructured.Unstructured{} @@ -147,7 +147,7 @@ func TestKubernetesService_UpdateFromList(t *testing.T) { "tinyauth.apps.myapp.users.allow": "alice", }) - svc.updateFromList([]unstructured.Unstructured{item}) + svc.updateFromItem(&item) got, ok := svc.getByDomain("myapp.example.com") require.True(t, ok) @@ -155,7 +155,7 @@ func TestKubernetesService_UpdateFromList(t *testing.T) { assert.Equal(t, "alice", got.Users.Allow) } -func TestKubernetesService_UpdateFromList_NoAnnotations(t *testing.T) { +func TestKubernetesService_UpdateFromItem_NoAnnotations(t *testing.T) { svc := newTestKubernetesService() // First add something to the cache @@ -169,7 +169,7 @@ func TestKubernetesService_UpdateFromList_NoAnnotations(t *testing.T) { item.SetNamespace("default") item.SetName("test-ingress") - svc.updateFromList([]unstructured.Unstructured{item}) + svc.updateFromItem(&item) _, ok := svc.getByDomain("todelete.example.com") assert.False(t, ok) From 8e42fe24064e627346c4cc2df158cb5e61f4a8a6 Mon Sep 17 00:00:00 2001 From: Contre Date: Wed, 22 Apr 2026 12:37:22 +0200 Subject: [PATCH 20/26] fix(refactor): Split watchGVR to make it more readable --- internal/service/kubernetes_service.go | 77 +++++++++++++------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index 30e8887e..a4da1a2b 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -171,11 +171,44 @@ func (k *KubernetesService) resyncGVR(gvr schema.GroupVersionResource) error { return nil } +// runWatcher drains events from an active watcher until it closes or the context is done. +// Returns true if the caller should restart the watcher, false if it should exit. +func (k *KubernetesService) runWatcher(gvr schema.GroupVersionResource, w watch.Interface, resyncTicker *time.Ticker) bool { + for { + select { + case <-k.ctx.Done(): + w.Stop() + return false + case event, ok := <-w.ResultChan(): + if !ok { + tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher channel closed, restarting in 5 seconds") + w.Stop() + time.Sleep(5 * time.Second) + return true + } + item, ok := event.Object.(*unstructured.Unstructured) + if !ok { + tlog.App.Warn().Str("api", gvr.GroupVersion().String()).Msg("Failed to cast watched object") + continue + } + switch event.Type { + case watch.Added, watch.Modified: + k.updateFromItem(item) + case watch.Deleted: + k.removeIngress(item.GetNamespace(), item.GetName()) + } + case <-resyncTicker.C: + if err := k.resyncGVR(gvr); err != nil { + tlog.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Periodic resync failed") + } + } + } +} + func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { resyncTicker := time.NewTicker(5 * time.Minute) defer resyncTicker.Stop() - // Initial resync if err := k.resyncGVR(gvr); err != nil { tlog.App.Error().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Initial resync failed, retrying in 30 seconds") time.Sleep(30 * time.Second) @@ -200,45 +233,11 @@ func (k *KubernetesService) watchGVR(gvr schema.GroupVersionResource) { continue } tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher started") - inner: - for { - select { - case <-k.ctx.Done(): - watcher.Stop() - cancel() - return - case event, ok := <-watcher.ResultChan(): - if !ok { - tlog.App.Debug().Str("api", gvr.GroupVersion().String()).Msg("Watcher channel closed, restarting in 5 seconds") - watcher.Stop() - cancel() - time.Sleep(5 * time.Second) - break inner - } - switch event.Type { - case watch.Added, watch.Modified: - item, ok := event.Object.(*unstructured.Unstructured) - if !ok { - tlog.App.Warn().Str("api", gvr.GroupVersion().String()).Msg("Failed to cast watched object") - continue - } - k.updateFromItem(item) - case watch.Deleted: - item, ok := event.Object.(*unstructured.Unstructured) - if !ok { - tlog.App.Warn().Str("api", gvr.GroupVersion().String()).Msg("Failed to cast watched object") - continue - } - k.removeIngress(item.GetNamespace(), item.GetName()) - default: - // ignore other event types - } - case <-resyncTicker.C: - if err := k.resyncGVR(gvr); err != nil { - tlog.App.Warn().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Periodic resync failed") - } - } + if !k.runWatcher(gvr, watcher, resyncTicker) { + cancel() + return } + cancel() } } } From f3ed816e545901d2815b32d46fcc6f6eceddab1c Mon Sep 17 00:00:00 2001 From: Contre Date: Wed, 22 Apr 2026 21:25:54 +0200 Subject: [PATCH 21/26] fix(refactor): Remove discovery + drop K 1.22 completely --- internal/service/kubernetes_service.go | 75 +++++--------------------- 1 file changed, 12 insertions(+), 63 deletions(-) diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index a4da1a2b..a05bf416 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -15,7 +15,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) @@ -41,7 +40,6 @@ type KubernetesService struct { ctx context.Context cancel context.CancelFunc started bool - v1GVR *schema.GroupVersionResource mu sync.RWMutex ingressApps map[ingressKey][]ingressApp domainIndex map[string]ingressAppKey @@ -256,69 +254,27 @@ func (k *KubernetesService) Init() error { return fmt.Errorf("failed to create Kubernetes client: %w", err) } - discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg) - if err != nil { - return fmt.Errorf("failed to create discovery client: %w", err) - } - k.client = client k.ctx, k.cancel = context.WithCancel(context.Background()) - // Check which Ingress APIs are available - apiGroups, err := discoveryClient.ServerPreferredResources() - if err != nil { - // This can happen with partial discovery errors, log and continue - tlog.App.Debug().Err(err).Msg("Failed to discover API resources") - } - - v1Available := false - for _, apiGroup := range apiGroups { - if apiGroup.GroupVersion == "networking.k8s.io/v1" { - for _, resource := range apiGroup.APIResources { - if resource.Name == "ingresses" && resource.Kind == "Ingress" { - v1Available = true - } - } - } - } - - // Check permissions for the v1 API - checkAccess := func(gvr schema.GroupVersionResource) bool { - ctx, cancel := context.WithTimeout(k.ctx, 5*time.Second) - defer cancel() - - _, err := k.client.Resource(gvr).List(ctx, metav1.ListOptions{Limit: 1}) - if err != nil { - tlog.App.Debug().Err(err).Str("api", gvr.GroupVersion().String()).Msg("Cannot access Ingress API") - return false - } - return true - } - - if v1Available { - gvr := schema.GroupVersionResource{ - Group: "networking.k8s.io", - Version: "v1", - Resource: "ingresses", - } - if checkAccess(gvr) { - tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API accessible") - k.v1GVR = &gvr - go k.watchIngressV1() - } else { - tlog.App.Warn().Msg("Insufficient permissions for networking.k8s.io/v1 Ingress") - v1Available = false - } - } else { - tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API not available") + gvr := schema.GroupVersionResource{ + Group: "networking.k8s.io", + Version: "v1", + Resource: "ingresses", } - if !v1Available { - tlog.App.Warn().Msg("No Ingress API available or accessible, Kubernetes label provider will not work") + accessCtx, accessCancel := context.WithTimeout(k.ctx, 5*time.Second) + defer accessCancel() + _, err = k.client.Resource(gvr).List(accessCtx, metav1.ListOptions{Limit: 1}) + if err != nil { + tlog.App.Warn().Err(err).Msg("Insufficient permissions for networking.k8s.io/v1 Ingress, Kubernetes label provider will not work") k.started = false return nil } + tlog.App.Debug().Msg("networking.k8s.io/v1 Ingress API accessible") + go k.watchGVR(gvr) + k.started = true tlog.App.Info().Msg("Kubernetes label provider initialized") return nil @@ -345,10 +301,3 @@ func (k *KubernetesService) GetLabels(appDomain string) (config.App, error) { return config.App{}, nil } -// watchIngressV1 starts watching networking.k8s.io/v1 ingresses -func (k *KubernetesService) watchIngressV1() { - if k.v1GVR == nil { - return - } - k.watchGVR(*k.v1GVR) -} From fec7473b8e84e44e9d9564c056ee1df446683fdb Mon Sep 17 00:00:00 2001 From: Contre Date: Wed, 22 Apr 2026 21:27:54 +0200 Subject: [PATCH 22/26] fix(refactor): Move interface to acess_controls_service --- internal/service/access_controls_service.go | 4 ++++ internal/service/label_provider.go | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) delete mode 100644 internal/service/label_provider.go diff --git a/internal/service/access_controls_service.go b/internal/service/access_controls_service.go index c9accda6..b4b93d9f 100644 --- a/internal/service/access_controls_service.go +++ b/internal/service/access_controls_service.go @@ -8,6 +8,10 @@ import ( "github.com/steveiliop56/tinyauth/internal/utils/tlog" ) +type LabelProvider interface { + GetLabels(appDomain string) (config.App, error) +} + type AccessControlsService struct { labelProvider LabelProvider static map[string]config.App diff --git a/internal/service/label_provider.go b/internal/service/label_provider.go deleted file mode 100644 index 9dc01bc9..00000000 --- a/internal/service/label_provider.go +++ /dev/null @@ -1,7 +0,0 @@ -package service - -import "github.com/steveiliop56/tinyauth/internal/config" - -type LabelProvider interface { - GetLabels(appDomain string) (config.App, error) -} From 095d86ff1dccca4bbb67b0783c4bbb1dcf6eed3a Mon Sep 17 00:00:00 2001 From: Contre Date: Wed, 22 Apr 2026 21:38:48 +0200 Subject: [PATCH 23/26] feat: Autodetect labelprovider if TINYAUTH_LABELPROVIDER not set --- internal/bootstrap/service_bootstrap.go | 12 +++++++++--- internal/config/config.go | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/internal/bootstrap/service_bootstrap.go b/internal/bootstrap/service_bootstrap.go index 43e8d878..3e9f2e4f 100644 --- a/internal/bootstrap/service_bootstrap.go +++ b/internal/bootstrap/service_bootstrap.go @@ -1,6 +1,8 @@ package bootstrap import ( + "os" + "github.com/steveiliop56/tinyauth/internal/repository" "github.com/steveiliop56/tinyauth/internal/service" "github.com/steveiliop56/tinyauth/internal/utils/tlog" @@ -43,8 +45,11 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er var dockerService *service.DockerService var kubernetesService *service.KubernetesService - switch app.config.LabelProvider { - case "kubernetes": + useKubernetes := app.config.LabelProvider == "kubernetes" || + (app.config.LabelProvider == "auto" && os.Getenv("KUBERNETES_SERVICE_HOST") != "") + + if useKubernetes { + tlog.App.Debug().Msg("Using Kubernetes label provider") kubernetesService = service.NewKubernetesService() err = kubernetesService.Init() if err != nil { @@ -52,7 +57,8 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er } services.kubernetesService = kubernetesService labelProvider = kubernetesService - default: + } else { + tlog.App.Debug().Msg("Using Docker label provider") dockerService = service.NewDockerService() err = dockerService.Init() if err != nil { diff --git a/internal/config/config.go b/internal/config/config.go index a62ca066..6dd44d43 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -59,7 +59,7 @@ func NewDefaultConfiguration() *Config { Experimental: ExperimentalConfig{ ConfigFile: "", }, - LabelProvider: "docker", + LabelProvider: "auto", } } @@ -90,7 +90,7 @@ type Config struct { UI UIConfig `description:"UI customization." yaml:"ui"` Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"` Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"` - LabelProvider string `description:"Label provider to use for ACLs (docker or kubernetes)." yaml:"labelProvider"` + LabelProvider string `description:"Label provider to use for ACLs (auto, docker, or kubernetes). auto detects the environment." yaml:"labelProvider"` Log LogConfig `description:"Logging configuration." yaml:"log"` } From 043c6706c3bdd8a0fe3eb5750346b994e3b4cd5a Mon Sep 17 00:00:00 2001 From: Contre Date: Fri, 24 Apr 2026 18:02:40 +0200 Subject: [PATCH 24/26] fix(test): Match testing scheme to the controllers --- internal/service/kubernetes_service_test.go | 330 ++++++++++---------- 1 file changed, 170 insertions(+), 160 deletions(-) diff --git a/internal/service/kubernetes_service_test.go b/internal/service/kubernetes_service_test.go index 0b374910..846370af 100644 --- a/internal/service/kubernetes_service_test.go +++ b/internal/service/kubernetes_service_test.go @@ -10,167 +10,177 @@ import ( "github.com/stretchr/testify/require" ) -func newTestKubernetesService() *KubernetesService { - return &KubernetesService{ - ingressApps: make(map[ingressKey][]ingressApp), - domainIndex: make(map[string]ingressAppKey), - appNameIndex: make(map[string]ingressAppKey), +func TestKubernetesService(t *testing.T) { + type testCase struct { + description string + run func(t *testing.T, svc *KubernetesService) } -} - -func TestKubernetesService_CacheByDomain(t *testing.T) { - svc := newTestKubernetesService() - - app := config.App{Config: config.AppConfig{Domain: "foo.example.com"}} - svc.addIngressApps("default", "my-ingress", []ingressApp{ - {domain: "foo.example.com", appName: "foo", app: app}, - }) - - got, ok := svc.getByDomain("foo.example.com") - require.True(t, ok) - assert.Equal(t, "foo.example.com", got.Config.Domain) - - _, ok = svc.getByDomain("notfound.example.com") - assert.False(t, ok) -} - -func TestKubernetesService_CacheByAppName(t *testing.T) { - svc := newTestKubernetesService() - - app := config.App{Config: config.AppConfig{Domain: "bar.example.com"}} - svc.addIngressApps("default", "my-ingress", []ingressApp{ - {domain: "bar.example.com", appName: "bar", app: app}, - }) - - got, ok := svc.getByAppName("bar") - require.True(t, ok) - assert.Equal(t, "bar.example.com", got.Config.Domain) - - _, ok = svc.getByAppName("notfound") - assert.False(t, ok) -} - -func TestKubernetesService_RemoveIngress(t *testing.T) { - svc := newTestKubernetesService() - - app := config.App{Config: config.AppConfig{Domain: "baz.example.com"}} - svc.addIngressApps("default", "my-ingress", []ingressApp{ - {domain: "baz.example.com", appName: "baz", app: app}, - }) - - svc.removeIngress("default", "my-ingress") - - _, ok := svc.getByDomain("baz.example.com") - assert.False(t, ok) - _, ok = svc.getByAppName("baz") - assert.False(t, ok) -} - -func TestKubernetesService_AddIngressApps_Replaces(t *testing.T) { - svc := newTestKubernetesService() - - old := config.App{Config: config.AppConfig{Domain: "old.example.com"}} - svc.addIngressApps("default", "my-ingress", []ingressApp{ - {domain: "old.example.com", appName: "old", app: old}, - }) - - updated := config.App{Config: config.AppConfig{Domain: "new.example.com"}} - svc.addIngressApps("default", "my-ingress", []ingressApp{ - {domain: "new.example.com", appName: "new", app: updated}, - }) - - // Old entry should be gone - _, ok := svc.getByDomain("old.example.com") - assert.False(t, ok) - - // New entry should be present - got, ok := svc.getByDomain("new.example.com") - require.True(t, ok) - assert.Equal(t, "new.example.com", got.Config.Domain) -} - -func TestKubernetesService_GetLabels_CacheHit(t *testing.T) { - svc := newTestKubernetesService() - svc.started = true - app := config.App{Config: config.AppConfig{Domain: "hit.example.com"}} - svc.addIngressApps("default", "ing", []ingressApp{ - {domain: "hit.example.com", appName: "hit", app: app}, - }) - - got, err := svc.GetLabels("hit.example.com") - require.NoError(t, err) - assert.Equal(t, "hit.example.com", got.Config.Domain) -} - -func TestKubernetesService_GetLabels_CacheMiss(t *testing.T) { - svc := newTestKubernetesService() - svc.started = true - - got, err := svc.GetLabels("notfound.example.com") - require.NoError(t, err) - assert.Equal(t, config.App{}, got) -} - -func TestKubernetesService_GetLabels_ByAppName(t *testing.T) { - svc := newTestKubernetesService() - svc.started = true - - app := config.App{Config: config.AppConfig{Domain: "myapp.internal.example.com"}} - svc.addIngressApps("default", "ing", []ingressApp{ - {domain: "myapp.internal.example.com", appName: "myapp", app: app}, - }) - - // Look up by subdomain prefix matching appName - got, err := svc.GetLabels("myapp.internal.example.com") - require.NoError(t, err) - assert.Equal(t, "myapp.internal.example.com", got.Config.Domain) -} - -func TestKubernetesService_GetLabels_NotStarted(t *testing.T) { - svc := newTestKubernetesService() - // started is false by default - - got, err := svc.GetLabels("anything.example.com") - require.NoError(t, err) - assert.Equal(t, config.App{}, got) -} - -func TestKubernetesService_UpdateFromItem(t *testing.T) { - svc := newTestKubernetesService() - - item := unstructured.Unstructured{} - item.SetNamespace("default") - item.SetName("test-ingress") - item.SetAnnotations(map[string]string{ - "tinyauth.apps.myapp.config.domain": "myapp.example.com", - "tinyauth.apps.myapp.users.allow": "alice", - }) - - svc.updateFromItem(&item) - - got, ok := svc.getByDomain("myapp.example.com") - require.True(t, ok) - assert.Equal(t, "myapp.example.com", got.Config.Domain) - assert.Equal(t, "alice", got.Users.Allow) -} - -func TestKubernetesService_UpdateFromItem_NoAnnotations(t *testing.T) { - svc := newTestKubernetesService() - - // First add something to the cache - app := config.App{Config: config.AppConfig{Domain: "todelete.example.com"}} - svc.addIngressApps("default", "test-ingress", []ingressApp{ - {domain: "todelete.example.com", appName: "todelete", app: app}, - }) - - // Now update with an item that has no annotations - item := unstructured.Unstructured{} - item.SetNamespace("default") - item.SetName("test-ingress") - - svc.updateFromItem(&item) + tests := []testCase{ + { + description: "Cache by domain returns app and misses unknown domain", + run: func(t *testing.T, svc *KubernetesService) { + app := config.App{Config: config.AppConfig{Domain: "foo.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "foo.example.com", appName: "foo", app: app}, + }) + + got, ok := svc.getByDomain("foo.example.com") + require.True(t, ok) + assert.Equal(t, "foo.example.com", got.Config.Domain) + + _, ok = svc.getByDomain("notfound.example.com") + assert.False(t, ok) + }, + }, + { + description: "Cache by app name returns app and misses unknown name", + run: func(t *testing.T, svc *KubernetesService) { + app := config.App{Config: config.AppConfig{Domain: "bar.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "bar.example.com", appName: "bar", app: app}, + }) + + got, ok := svc.getByAppName("bar") + require.True(t, ok) + assert.Equal(t, "bar.example.com", got.Config.Domain) + + _, ok = svc.getByAppName("notfound") + assert.False(t, ok) + }, + }, + { + description: "RemoveIngress clears domain and app name entries", + run: func(t *testing.T, svc *KubernetesService) { + app := config.App{Config: config.AppConfig{Domain: "baz.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "baz.example.com", appName: "baz", app: app}, + }) + + svc.removeIngress("default", "my-ingress") + + _, ok := svc.getByDomain("baz.example.com") + assert.False(t, ok) + _, ok = svc.getByAppName("baz") + assert.False(t, ok) + }, + }, + { + description: "AddIngressApps replaces stale entries for the same ingress", + run: func(t *testing.T, svc *KubernetesService) { + old := config.App{Config: config.AppConfig{Domain: "old.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "old.example.com", appName: "old", app: old}, + }) + + updated := config.App{Config: config.AppConfig{Domain: "new.example.com"}} + svc.addIngressApps("default", "my-ingress", []ingressApp{ + {domain: "new.example.com", appName: "new", app: updated}, + }) + + _, ok := svc.getByDomain("old.example.com") + assert.False(t, ok) + + got, ok := svc.getByDomain("new.example.com") + require.True(t, ok) + assert.Equal(t, "new.example.com", got.Config.Domain) + }, + }, + { + description: "GetLabels returns app from cache when started", + run: func(t *testing.T, svc *KubernetesService) { + svc.started = true + + app := config.App{Config: config.AppConfig{Domain: "hit.example.com"}} + svc.addIngressApps("default", "ing", []ingressApp{ + {domain: "hit.example.com", appName: "hit", app: app}, + }) + + got, err := svc.GetLabels("hit.example.com") + require.NoError(t, err) + assert.Equal(t, "hit.example.com", got.Config.Domain) + }, + }, + { + description: "GetLabels returns empty app on cache miss when started", + run: func(t *testing.T, svc *KubernetesService) { + svc.started = true + + got, err := svc.GetLabels("notfound.example.com") + require.NoError(t, err) + assert.Equal(t, config.App{}, got) + }, + }, + { + description: "GetLabels resolves app by app name", + run: func(t *testing.T, svc *KubernetesService) { + svc.started = true + + app := config.App{Config: config.AppConfig{Domain: "myapp.internal.example.com"}} + svc.addIngressApps("default", "ing", []ingressApp{ + {domain: "myapp.internal.example.com", appName: "myapp", app: app}, + }) + + got, err := svc.GetLabels("myapp.internal.example.com") + require.NoError(t, err) + assert.Equal(t, "myapp.internal.example.com", got.Config.Domain) + }, + }, + { + description: "GetLabels returns empty app when service not yet started", + run: func(t *testing.T, svc *KubernetesService) { + got, err := svc.GetLabels("anything.example.com") + require.NoError(t, err) + assert.Equal(t, config.App{}, got) + }, + }, + { + description: "UpdateFromItem parses annotations and populates cache", + run: func(t *testing.T, svc *KubernetesService) { + item := unstructured.Unstructured{} + item.SetNamespace("default") + item.SetName("test-ingress") + item.SetAnnotations(map[string]string{ + "tinyauth.apps.myapp.config.domain": "myapp.example.com", + "tinyauth.apps.myapp.users.allow": "alice", + }) + + svc.updateFromItem(&item) + + got, ok := svc.getByDomain("myapp.example.com") + require.True(t, ok) + assert.Equal(t, "myapp.example.com", got.Config.Domain) + assert.Equal(t, "alice", got.Users.Allow) + }, + }, + { + description: "UpdateFromItem with no annotations removes existing cache entries", + run: func(t *testing.T, svc *KubernetesService) { + app := config.App{Config: config.AppConfig{Domain: "todelete.example.com"}} + svc.addIngressApps("default", "test-ingress", []ingressApp{ + {domain: "todelete.example.com", appName: "todelete", app: app}, + }) + + item := unstructured.Unstructured{} + item.SetNamespace("default") + item.SetName("test-ingress") + + svc.updateFromItem(&item) + + _, ok := svc.getByDomain("todelete.example.com") + assert.False(t, ok) + }, + }, + } - _, ok := svc.getByDomain("todelete.example.com") - assert.False(t, ok) + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + svc := &KubernetesService{ + ingressApps: make(map[ingressKey][]ingressApp), + domainIndex: make(map[string]ingressAppKey), + appNameIndex: make(map[string]ingressAppKey), + } + test.run(t, svc) + }) + } } From 5a95c055811eb1d18d7d149755d9c8fc622ad6c0 Mon Sep 17 00:00:00 2001 From: Contre Date: Mon, 27 Apr 2026 20:18:20 +0200 Subject: [PATCH 25/26] fix: service bootstrap import after merge --- internal/bootstrap/service_bootstrap.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/bootstrap/service_bootstrap.go b/internal/bootstrap/service_bootstrap.go index 544b5b64..91e2b50b 100644 --- a/internal/bootstrap/service_bootstrap.go +++ b/internal/bootstrap/service_bootstrap.go @@ -3,9 +3,6 @@ package bootstrap import ( "os" - "github.com/steveiliop56/tinyauth/internal/repository" - "github.com/steveiliop56/tinyauth/internal/service" - "github.com/steveiliop56/tinyauth/internal/utils/tlog "github.com/tinyauthapp/tinyauth/internal/repository" "github.com/tinyauthapp/tinyauth/internal/service" "github.com/tinyauthapp/tinyauth/internal/utils/tlog" From 7ca1397a369eb9b258d4130b2f645e458a3be4ac Mon Sep 17 00:00:00 2001 From: Contre Date: Mon, 27 Apr 2026 20:31:04 +0200 Subject: [PATCH 26/26] fix: service bootstrap import after merge --- go.mod | 10 ---------- go.sum | 14 -------------- internal/service/kubernetes_service.go | 6 +++--- internal/service/kubernetes_service_test.go | 2 +- 4 files changed, 4 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 2a968ab0..0710072c 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,6 @@ require ( github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect @@ -72,26 +71,19 @@ require ( github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.20 // indirect @@ -141,9 +133,7 @@ require ( google.golang.org/protobuf v1.36.11 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect modernc.org/libc v1.72.0 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index 93bc6b59..ee982c89 100644 --- a/go.sum +++ b/go.sum @@ -81,7 +81,6 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -123,12 +122,10 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/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-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 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.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.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -139,8 +136,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= -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.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= @@ -194,11 +189,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -246,11 +238,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -291,7 +278,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ 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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= diff --git a/internal/service/kubernetes_service.go b/internal/service/kubernetes_service.go index a05bf416..6e11eac1 100644 --- a/internal/service/kubernetes_service.go +++ b/internal/service/kubernetes_service.go @@ -7,9 +7,9 @@ import ( "sync" "time" - "github.com/steveiliop56/tinyauth/internal/config" - "github.com/steveiliop56/tinyauth/internal/utils/decoders" - "github.com/steveiliop56/tinyauth/internal/utils/tlog" + "github.com/tinyauthapp/tinyauth/internal/config" + "github.com/tinyauthapp/tinyauth/internal/utils/decoders" + "github.com/tinyauthapp/tinyauth/internal/utils/tlog" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/internal/service/kubernetes_service_test.go b/internal/service/kubernetes_service_test.go index 846370af..1cd75b6a 100644 --- a/internal/service/kubernetes_service_test.go +++ b/internal/service/kubernetes_service_test.go @@ -3,7 +3,7 @@ package service import ( "testing" - "github.com/steveiliop56/tinyauth/internal/config" + "github.com/tinyauthapp/tinyauth/internal/config" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/stretchr/testify/assert"