From 9054718a4b07a5bf74ae1e7d87a585ba4bec1203 Mon Sep 17 00:00:00 2001 From: George MacRorie Date: Sun, 5 May 2024 17:52:25 +0100 Subject: [PATCH 1/4] feat(config): support fetching and watching configmaps for tunnel groups --- README.md | 31 ++++++++- cmd/reverst/main.go | 68 ++++--------------- go.mod | 36 +++++++++- go.sum | 111 ++++++++++++++++++++++++++++++ go.work | 4 +- go.work.sum | 14 ++-- internal/config/config.go | 39 +++++++---- internal/config/fsnotify.go | 91 +++++++++++++++++++++++++ internal/config/k8s.go | 74 ++++++++++++++++++++ internal/k8s/k8s.go | 132 ++++++++++++++++++++++++++++++++++++ internal/server/server.go | 2 +- 11 files changed, 515 insertions(+), 87 deletions(-) create mode 100644 internal/config/fsnotify.go create mode 100644 internal/config/k8s.go create mode 100644 internal/k8s/k8s.go diff --git a/README.md b/README.md index 302f6a1..3126331 100644 --- a/README.md +++ b/README.md @@ -120,11 +120,13 @@ USAGE FLAGS -l, --log LEVEL debug, info, warn or error (default: INFO) -a, --tunnel-address STRING address for accepting tunnelling quic connections (default: 127.0.0.1:7171) - -s, --http-address STRING address for serving HTTP requests (default: 127.0.0.1:8181) - -g, --tunnel-groups STRING path to tunnel groups configuration file (default: groups.yml) + -s, --http-address STRING address for serving HTTP requests (default: 0.0.0.0:8181) -n, --server-name STRING server name used to identify tunnel via TLS (required) -k, --private-key-path STRING path to TLS private key PEM file (required) -c, --certificate-path STRING path to TLS certificate PEM file (required) + -g, --tunnel-groups STRING path to file or k8s configmap identifier (default: groups.yml) + -w, --watch-groups watch tunnel groups sources for updates + --management-address STRING HTTP address for management API --max-idle-timeout DURATION maximum time a connection can be idle (default: 1m0s) --keep-alive-period DURATION period between keep-alive events (default: 30s) ``` @@ -136,6 +138,31 @@ For example, `--tunnel-address` becomes `REVERST_TUNNEL_ADDRESS`. #### Tunnel Groups Configuration YAML +**configuring** + +Currently, the tunnel groups configuration can be sourced from two different locations types (`file` and `k8s`). +Both tunnel group sources support watching sources for changes over time (see `-w` flag). + +- Local filesystem (`file://[path]`) + +The standard and simplest method is to point reverst at your configuration YAML file on your machine via its path. + +```console +reverst -g path/to/configuration.yml +// alternatively: +reverst -g file:///path/to/configuration.yml +``` + +- Kubernetes ConfigMap `k8s://configmap/[namespace]/[name]/[key]` + +Alternatively, you can configure reverst to connect to a Kubernetes API server and fetch / watch configuration from. + +```console +reverst -g k8s://configmap/default/tunnelconfig/groups.yml +``` + +**defining** + The reverst server take a path to a YAML encoded file, which identifies the tunnel groups to be hosted. A tunnel group is a load-balancer on which tunneled servers can register themselves. The file contains a top-level key groups, under which each tunnel group is uniquely named. diff --git a/cmd/reverst/main.go b/cmd/reverst/main.go index b3d2c85..ce71a80 100644 --- a/cmd/reverst/main.go +++ b/cmd/reverst/main.go @@ -9,8 +9,8 @@ import ( "os" "os/signal" "syscall" + "time" - "github.com/fsnotify/fsnotify" "github.com/peterbourgon/ff/v4" "github.com/peterbourgon/ff/v4/ffhelp" "go.flipt.io/reverst/internal/config" @@ -38,62 +38,18 @@ func main() { return err } - groups, err := conf.TunnelGroups() - if err != nil { - return err - } - + // start a subscription for tunnel group configuration + // this function should push at-least one tunnel groups + // instance on the channel before returning a non-nil error groupsChan := make(chan *config.TunnelGroups, 1) - groupsChan <- groups - - if conf.WatchTunnelGroups { - watcher, err := fsnotify.NewWatcher() - if err != nil { - return err - } - defer watcher.Close() - - go func() { - defer close(groupsChan) - - for { - select { - case event, ok := <-watcher.Events: - if !ok { - return - } - - slog.Debug("Watcher event", "event", event) - - if !(event.Has(fsnotify.Remove)) { - continue - } - - groups, err := conf.TunnelGroups() - if err != nil { - slog.Error("reading tunnel groups: %w", err) - continue - } - - groupsChan <- groups - - // remove and re-add as the file has been moved atomically - watcher.Remove(event.Name) - watcher.Add(conf.TunnelGroupsPath) - - case err, ok := <-watcher.Errors: - if !ok { - return - } - - slog.Error("watching tunnel groups", "error", err) - } - } - }() - - if err := watcher.Add(conf.TunnelGroupsPath); err != nil { - return err - } + if err := func() error { + // this anonymous function allows us to defer a close + // and safely shadow the parent context + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + return conf.SubscribeTunnelGroups(ctx, groupsChan) + }(); err != nil { + return err } server, err := server.New(conf, groupsChan) diff --git a/go.mod b/go.mod index 77bd820..4756350 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,11 @@ module go.flipt.io/reverst -go 1.21.4 +go 1.22.0 + +toolchain go1.22.2 require ( + github.com/fsnotify/fsnotify v1.7.0 github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 github.com/prometheus/client_golang v1.19.0 github.com/quic-go/quic-go v0.43.0 @@ -14,17 +17,34 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.25.0 golang.org/x/sync v0.7.0 gopkg.in/yaml.v2 v2.4.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/go-task/slim-sprig/v3 v3.0.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.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20240430035430-e4905b036c4e // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo/v2 v2.17.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -39,9 +59,21 @@ require ( golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.24.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.20.0 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.30.0 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 3dc2b95..15c24f4 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,14 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -11,16 +17,53 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/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 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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-20240430035430-e4905b036c4e h1:RsXNnXE59RTt8o3DcA+w7ICdRfR2l+Bb5aE0YMpNTO8= github.com/google/pprof v0.0.0-20240430035430-e4905b036c4e/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= @@ -29,6 +72,8 @@ github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3UO8QfUg0Z7Y= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= @@ -45,12 +90,23 @@ github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/ github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= go.opentelemetry.io/otel/exporters/prometheus v0.47.0 h1:OL6yk1Z/pEGdDnrBbxSsH+t4FY1zXfBRGd7bjwhlMLU= @@ -65,30 +121,85 @@ go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1 go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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/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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/go.work b/go.work index 4ea6608..c8c05aa 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,6 @@ -go 1.21.7 +go 1.22.0 -toolchain go1.21.8 +toolchain go1.22.2 use ( . diff --git a/go.work.sum b/go.work.sum index 7db37ca..3e494a9 100644 --- a/go.work.sum +++ b/go.work.sum @@ -26,6 +26,7 @@ github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -43,16 +44,15 @@ github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnx github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kevinmbeaulieu/eq-go v1.0.0 h1:AQgYHURDOmnVJ62jnEk0W/7yFKEn+Lv8RHN6t7mB0Zo= github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matryer/moq v0.2.7 h1:RtpiPUM8L7ZSCbSwK+QcZH/E9tgqAkFjKQxsRs25b4w= github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -62,8 +62,6 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -77,21 +75,19 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= diff --git a/internal/config/config.go b/internal/config/config.go index 393f8b2..1b28550 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,32 +1,34 @@ package config import ( + "context" "encoding/hex" "errors" "fmt" "log/slog" "net/url" "os" + "path/filepath" "reflect" + "strings" "time" "go.flipt.io/reverst/internal/auth" "go.flipt.io/reverst/pkg/protocol" - "gopkg.in/yaml.v2" ) type Config struct { Level Level `ff:" short=l | long=log | default=info | usage: 'debug, info, warn or error' "` TunnelAddress string `ff:" short=a | long=tunnel-address | default='127.0.0.1:7171' | usage: address for accepting tunnelling quic connections "` HTTPAddress string `ff:" short=s | long=http-address | default='0.0.0.0:8181' | usage: address for serving HTTP requests "` - TunnelGroupsPath string `ff:" short=g | long=tunnel-groups | default='groups.yml' | usage: path to tunnel groups configuration file "` - WatchTunnelGroups bool `ff:" short=w | long=watch-groups | default=false | usage: watch tunnel groups file for updates "` ServerName string `ff:" short=n | long=server-name | usage: server name used to identify tunnel via TLS (required) "` PrivateKeyPath string `ff:" short=k | long=private-key-path | usage: path to TLS private key PEM file (required) "` CertificatePath string `ff:" short=c | long=certificate-path | usage: path to TLS certificate PEM file (required) "` + TunnelGroups string `ff:" short=g | long=tunnel-groups | default='groups.yml' | usage: path to file or k8s configmap identifier "` + WatchTunnelGroups bool `ff:" short=w | long=watch-groups | default=false | usage: watch tunnel groups sources for updates "` // ManagementAddress is where reverst hosts introspective APIs for telemetry and debugging etc. - ManagementAddress string `ff:" long=management-address | usage: HTTP address for managment API "` + ManagementAddress string `ff:" long=management-address | usage: HTTP address for management API "` MaxIdleTimeout time.Duration `ff:" long=max-idle-timeout | default=1m | usage: maximum time a connection can be idle "` KeepAlivePeriod time.Duration `ff:" long=keep-alive-period | default=30s | usage: period between keep-alive events "` @@ -48,24 +50,31 @@ func (c Config) Validate() error { return nil } -func (c Config) TunnelGroups() (*TunnelGroups, error) { - fi, err := os.Open(c.TunnelGroupsPath) +func (c *Config) SubscribeTunnelGroups(ctx context.Context, ch chan<- *TunnelGroups) error { + defer close(ch) + + u, err := url.Parse(c.TunnelGroups) if err != nil { - return nil, fmt.Errorf("reading tunnel groups: %w", err) + return err } - defer fi.Close() + switch u.Scheme { + case "", "file": + return watchFSNotify(ctx, ch, filepath.Join(u.Host, u.Path)) + case "k8s": + if u.Host == "configmap" { + parts := strings.SplitN(strings.TrimPrefix(u.Path, "/"), "/", 3) + if len(parts) < 3 { + return fmt.Errorf("unexpected k8s configmap path: %q (should be [namespace]/[name]/[key])", u.Path) + } - var groups TunnelGroups - if err := yaml.NewDecoder(fi).Decode(&groups); err != nil { - return nil, fmt.Errorf("decoding tunnel groups: %w", err) - } + return watchK8sConfigMap(ctx, ch, parts[0], parts[1], parts[2]) + } - if err := groups.Validate(); err != nil { - return nil, fmt.Errorf("validating tunnel groups: %w", err) + return fmt.Errorf("unsupported k8s resource: %q (expected [configmap])", u.Host) } - return &groups, nil + return fmt.Errorf("unexpected tunnel group scheme: %q", c.TunnelGroups) } // TunnelGroups is a configuration file format for defining the tunnel diff --git a/internal/config/fsnotify.go b/internal/config/fsnotify.go new file mode 100644 index 0000000..187ac63 --- /dev/null +++ b/internal/config/fsnotify.go @@ -0,0 +1,91 @@ +package config + +import ( + "context" + "fmt" + "log/slog" + "os" + + "github.com/fsnotify/fsnotify" + "gopkg.in/yaml.v2" +) + +func watchFSNotify(ctx context.Context, ch chan<- *TunnelGroups, path string) error { + groups, err := buildTunnelGroupsAtPath(path) + if err != nil { + return err + } + + // feed initial channel group + ch <- groups + + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + defer watcher.Close() + + go func() { + defer close(ch) + + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + + slog.Debug("Watcher event", "event", event) + + if !(event.Has(fsnotify.Remove)) { + continue + } + + groups, err := buildTunnelGroupsAtPath(path) + if err != nil { + slog.Error("reading tunnel groups: %w", err) + continue + } + + ch <- groups + + // remove and re-add as the file has been moved atomically + watcher.Remove(event.Name) + watcher.Add(path) + + case err, ok := <-watcher.Errors: + if !ok { + return + } + + slog.Error("watching tunnel groups", "error", err) + } + } + }() + + if err := watcher.Add(path); err != nil { + return err + } + + return nil +} + +func buildTunnelGroupsAtPath(path string) (*TunnelGroups, error) { + fi, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("reading tunnel groups: %w", err) + } + + defer fi.Close() + + var groups TunnelGroups + if err := yaml.NewDecoder(fi).Decode(&groups); err != nil { + return nil, fmt.Errorf("decoding tunnel groups: %w", err) + } + + if err := groups.Validate(); err != nil { + return nil, fmt.Errorf("validating tunnel groups: %w", err) + } + + return &groups, nil +} diff --git a/internal/config/k8s.go b/internal/config/k8s.go new file mode 100644 index 0000000..5570dad --- /dev/null +++ b/internal/config/k8s.go @@ -0,0 +1,74 @@ +package config + +import ( + "context" + "fmt" + "log/slog" + + "go.flipt.io/reverst/internal/k8s" + "gopkg.in/yaml.v2" + v1 "k8s.io/api/core/v1" +) + +func watchK8sConfigMap(ctx context.Context, ch chan<- *TunnelGroups, namespace, name, key string) error { + cfgs := make(chan v1.ConfigMap) + + if err := k8s.WatchConfigMap(ctx, cfgs, namespace, name); err != nil { + return err + } + + select { + case <-ctx.Done(): + return ctx.Err() + case cfg := <-cfgs: + groups, err := buildTunnelGroupsFromConfigMap(cfg, key) + if err != nil { + return err + } + + ch <- groups + } + + go func() { + defer close(cfgs) + + for { + select { + case <-ctx.Done(): + return + case cfg, ok := <-cfgs: + if !ok { + return + } + + groups, err := buildTunnelGroupsFromConfigMap(cfg, key) + if err != nil { + slog.Error("Building tunnel groups from ConfigMap", "error", err, "namespace", namespace, "name", name) + continue + } + + ch <- groups + } + } + }() + + return nil +} + +func buildTunnelGroupsFromConfigMap(cfg v1.ConfigMap, key string) (*TunnelGroups, error) { + raw, ok := cfg.Data[key] + if !ok { + return nil, fmt.Errorf("key %q not found in ConfigMap", key) + } + + var groups TunnelGroups + if err := yaml.Unmarshal([]byte(raw), &groups); err != nil { + return nil, fmt.Errorf("decoding tunnel groups: %w", err) + } + + if err := groups.Validate(); err != nil { + return nil, fmt.Errorf("validating tunnel groups: %w", err) + } + + return &groups, nil +} diff --git a/internal/k8s/k8s.go b/internal/k8s/k8s.go new file mode 100644 index 0000000..c199960 --- /dev/null +++ b/internal/k8s/k8s.go @@ -0,0 +1,132 @@ +package k8s + +import ( + "context" + "fmt" + "log/slog" + "os" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +// WatchConfigMap pushes instances of the identified configuration map found in a +// target Kuberentes cluster. +func WatchConfigMap(ctx context.Context, dst chan<- v1.ConfigMap, namespace, name string) error { + k8sConfig, err := k8sConfig() + if err != nil { + return err + } + + client, err := dynamic.NewForConfig(k8sConfig) + if err != nil { + return err + } + + log := slog.With("namespace", namespace, "name", name) + log.Debug("Creating shared informer factory") + + factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(client, 0, namespace, func(lo *metav1.ListOptions) { + lo.FieldSelector = fields.OneTermEqualSelector("metadata.name", name).String() + }) + + if err := inform(ctx, factory, v1.SchemeGroupVersion.WithResource("configmaps"), TypedEventHandler[v1.ConfigMap]{ + AddFunc: func(cfg v1.ConfigMap) { + log.Debug("ConfigMap added") + dst <- cfg + }, + UpdateFunc: func(_, cfg v1.ConfigMap) { + log.Debug("ConfigMap updated") + dst <- cfg + }, + DeleteFunc: func(cfg v1.ConfigMap) { + log.Debug("ConfigMap deleted") + }, + }); err != nil { + return err + } + + return nil +} + +func k8sConfig() (*rest.Config, error) { + if cfg := os.Getenv("KUBECONFIG"); cfg != "" { + return clientcmd.BuildConfigFromFlags("", cfg) + } + + return rest.InClusterConfig() +} + +func inform[T any](ctx context.Context, factory dynamicinformer.DynamicSharedInformerFactory, resource schema.GroupVersionResource, handler TypedEventHandler[T]) error { + informer := factory.ForResource(resource).Informer() + if _, err := informer.AddEventHandler(handler); err != nil { + return err + } + + go informer.Run(ctx.Done()) + + return nil +} + +type TypedEventHandler[T any] struct { + AddFunc func(T) + UpdateFunc func(T, T) + DeleteFunc func(T) +} + +func (_ TypedEventHandler[T]) parse(obj any) (t T, err error) { + err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.(*unstructured.Unstructured).Object, &t) + if err != nil { + slog.Error("Parsing object into type", "type", fmt.Sprintf("%T", t), "error", err) + } + return +} + +// OnAdd calls AddFunc if it's not nil. +func (t TypedEventHandler[T]) OnAdd(obj interface{}, isInInitialList bool) { + if t.AddFunc != nil { + o, err := t.parse(obj) + if err != nil { + return + } + + t.AddFunc(o) + } +} + +// OnUpdate calls UpdateFunc if it's not nil. +func (t TypedEventHandler[T]) OnUpdate(oldObj, newObj interface{}) { + if t.UpdateFunc != nil { + o, err := t.parse(oldObj) + if err != nil { + return + } + + n, err := t.parse(newObj) + if err != nil { + return + } + + t.UpdateFunc(o, n) + } +} + +// OnDelete calls DeleteFunc if it's not nil. +func (t TypedEventHandler[T]) OnDelete(obj interface{}) { + if t.DeleteFunc != nil { + o, err := t.parse(obj) + if err != nil { + return + } + + t.DeleteFunc(o) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index e28031e..9346e68 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -115,7 +115,7 @@ func New(conf config.Config, groupChan <-chan *config.TunnelGroups) (*Server, er } go func() { - log := slog.With("tunnel_groups_path", conf.TunnelGroupsPath) + log := slog.With("tunnel_groups_path", conf.TunnelGroups) defer log.Info("Closing tunnel groups watcher") for groups := range groupChan { From f7e3e479274a11d2b272728d633de5efefaf65c1 Mon Sep 17 00:00:00 2001 From: George MacRorie Date: Sun, 5 May 2024 17:57:17 +0100 Subject: [PATCH 2/4] fix(config): stop following after one config if watch not configured --- internal/config/config.go | 4 ++-- internal/config/fsnotify.go | 6 +++++- internal/config/k8s.go | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 1b28550..de080fd 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -60,7 +60,7 @@ func (c *Config) SubscribeTunnelGroups(ctx context.Context, ch chan<- *TunnelGro switch u.Scheme { case "", "file": - return watchFSNotify(ctx, ch, filepath.Join(u.Host, u.Path)) + return watchFSNotify(ctx, ch, filepath.Join(u.Host, u.Path), c.WatchTunnelGroups) case "k8s": if u.Host == "configmap" { parts := strings.SplitN(strings.TrimPrefix(u.Path, "/"), "/", 3) @@ -68,7 +68,7 @@ func (c *Config) SubscribeTunnelGroups(ctx context.Context, ch chan<- *TunnelGro return fmt.Errorf("unexpected k8s configmap path: %q (should be [namespace]/[name]/[key])", u.Path) } - return watchK8sConfigMap(ctx, ch, parts[0], parts[1], parts[2]) + return watchK8sConfigMap(ctx, ch, parts[0], parts[1], parts[2], c.WatchTunnelGroups) } return fmt.Errorf("unsupported k8s resource: %q (expected [configmap])", u.Host) diff --git a/internal/config/fsnotify.go b/internal/config/fsnotify.go index 187ac63..a915319 100644 --- a/internal/config/fsnotify.go +++ b/internal/config/fsnotify.go @@ -10,7 +10,7 @@ import ( "gopkg.in/yaml.v2" ) -func watchFSNotify(ctx context.Context, ch chan<- *TunnelGroups, path string) error { +func watchFSNotify(ctx context.Context, ch chan<- *TunnelGroups, path string, watch bool) error { groups, err := buildTunnelGroupsAtPath(path) if err != nil { return err @@ -19,6 +19,10 @@ func watchFSNotify(ctx context.Context, ch chan<- *TunnelGroups, path string) er // feed initial channel group ch <- groups + if !watch { + return nil + } + watcher, err := fsnotify.NewWatcher() if err != nil { return err diff --git a/internal/config/k8s.go b/internal/config/k8s.go index 5570dad..f2bc33e 100644 --- a/internal/config/k8s.go +++ b/internal/config/k8s.go @@ -10,7 +10,7 @@ import ( v1 "k8s.io/api/core/v1" ) -func watchK8sConfigMap(ctx context.Context, ch chan<- *TunnelGroups, namespace, name, key string) error { +func watchK8sConfigMap(ctx context.Context, ch chan<- *TunnelGroups, namespace, name, key string, watch bool) error { cfgs := make(chan v1.ConfigMap) if err := k8s.WatchConfigMap(ctx, cfgs, namespace, name); err != nil { @@ -29,6 +29,12 @@ func watchK8sConfigMap(ctx context.Context, ch chan<- *TunnelGroups, namespace, ch <- groups } + if !watch { + close(cfgs) + close(ch) + return nil + } + go func() { defer close(cfgs) From aeaa0fceccdba4d141665c117961c69c75dfc334 Mon Sep 17 00:00:00 2001 From: George MacRorie Date: Sun, 5 May 2024 21:21:11 +0100 Subject: [PATCH 3/4] fix(dagger): upgrade to Go 1.22 --- dagger/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dagger/main.go b/dagger/main.go index b9edae5..7bf4548 100644 --- a/dagger/main.go +++ b/dagger/main.go @@ -41,7 +41,7 @@ func (m *Reverst) BuildContainer( ) (*Container, error) { build := dag. Go(). - FromVersion("1.21-alpine3.18"). + FromVersion("1.22-alpine3.18"). Build(source, dagger.GoBuildOpts{ Packages: []string{"./cmd/reverst/..."}, }) @@ -58,7 +58,7 @@ func (m *Reverst) TestUnit( source *dagger.Directory, ) (string, error) { out, err := dag.Container(). - From("golang:1.21-alpine3.18"). + From("golang:1.22-alpine3.18"). WithExec([]string{"apk", "add", "gcc", "build-base"}). With(dag.Go().GlobalCache). WithEnvVariable("CGO_ENABLED", "1"). @@ -128,7 +128,7 @@ func (m *Reverst) TestIntegration( } out, err := dag.Container(). - From("golang:1.21-alpine3.18"). + From("golang:1.22-alpine3.18"). WithServiceBinding("local.example", reverst). With(dag.Go().GlobalCache). WithMountedDirectory("/src", source). From 57e25f88a2b31a83ec1a2342094c0f0a7f1fd0d9 Mon Sep 17 00:00:00 2001 From: George MacRorie Date: Sun, 5 May 2024 21:28:10 +0100 Subject: [PATCH 4/4] fix(config): move tunnel group channel closing into watcher routine --- internal/config/config.go | 2 -- internal/config/k8s.go | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index de080fd..b024510 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -51,8 +51,6 @@ func (c Config) Validate() error { } func (c *Config) SubscribeTunnelGroups(ctx context.Context, ch chan<- *TunnelGroups) error { - defer close(ch) - u, err := url.Parse(c.TunnelGroups) if err != nil { return err diff --git a/internal/config/k8s.go b/internal/config/k8s.go index f2bc33e..f416522 100644 --- a/internal/config/k8s.go +++ b/internal/config/k8s.go @@ -36,6 +36,7 @@ func watchK8sConfigMap(ctx context.Context, ch chan<- *TunnelGroups, namespace, } go func() { + defer close(ch) defer close(cfgs) for {