From 435cb4eb9808bde51e594a9d8bf18449203560b9 Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Wed, 16 Aug 2023 12:45:07 -0400 Subject: [PATCH 1/9] add retry package (#4458) --- internal/retry/config.go | 76 +++++++++++++++++++ internal/retry/error.go | 48 ++++++++++++ internal/retry/error_test.go | 29 ++++++++ internal/retry/retry.go | 139 +++++++++++++++++++++++++++++++++++ internal/retry/retry_test.go | 124 +++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 internal/retry/config.go create mode 100644 internal/retry/error.go create mode 100644 internal/retry/error_test.go create mode 100644 internal/retry/retry.go create mode 100644 internal/retry/retry_test.go diff --git a/internal/retry/config.go b/internal/retry/config.go new file mode 100644 index 00000000000..a5929c77f20 --- /dev/null +++ b/internal/retry/config.go @@ -0,0 +1,76 @@ +package retry + +import ( + "context" + "reflect" + "time" + + "github.com/cenkalti/backoff/v4" +) + +type config struct { + maxInterval time.Duration + watches []watch + + backoff.BackOff +} + +// watch is a helper struct to watch multiple channels +type watch struct { + name string + ch reflect.Value + fn func(context.Context) error + this bool +} + +// Option configures the retry handler +type Option func(*config) + +// WithWatch adds a watch to the retry handler +// that will be triggered when a value is received on the channel +// and the function will be called, also within a retry handler +func WithWatch[T any](name string, ch <-chan T, fn func(context.Context) error) Option { + return func(cfg *config) { + cfg.watches = append(cfg.watches, watch{name: name, ch: reflect.ValueOf(ch), fn: fn, this: false}) + } +} + +// WithMaxInterval sets the upper bound for the retry handler +func WithMaxInterval(d time.Duration) Option { + return func(cfg *config) { + cfg.maxInterval = d + } +} + +func newConfig(opts ...Option) ([]watch, backoff.BackOff) { + cfg := new(config) + for _, opt := range []Option{ + WithMaxInterval(time.Minute * 5), + } { + opt(cfg) + } + + for _, opt := range opts { + opt(cfg) + } + + for i, w := range cfg.watches { + cfg.watches[i].fn = withRetry(cfg, w) + } + + bo := backoff.NewExponentialBackOff() + bo.MaxInterval = cfg.maxInterval + bo.MaxElapsedTime = 0 + + return cfg.watches, bo +} + +func withRetry(cfg *config, w watch) func(context.Context) error { + if w.fn == nil { + return func(_ context.Context) error { return nil } + } + + return func(ctx context.Context) error { + return Retry(ctx, w.name, w.fn, WithMaxInterval(cfg.maxInterval)) + } +} diff --git a/internal/retry/error.go b/internal/retry/error.go new file mode 100644 index 00000000000..bc7d25a31de --- /dev/null +++ b/internal/retry/error.go @@ -0,0 +1,48 @@ +package retry + +import ( + "errors" + "fmt" +) + +// TerminalError is an error that should not be retried +type TerminalError interface { + error + IsTerminal() +} + +// terminalError is an error that should not be retried +type terminalError struct { + Err error +} + +// Error implements error for terminalError +func (e *terminalError) Error() string { + return fmt.Sprintf("terminal error: %v", e.Err) +} + +// Unwrap implements errors.Unwrap for terminalError +func (e *terminalError) Unwrap() error { + return e.Err +} + +// Is implements errors.Is for terminalError +func (e *terminalError) Is(err error) bool { + //nolint:errorlint + _, ok := err.(*terminalError) + return ok +} + +// IsTerminal implements TerminalError for terminalError +func (e *terminalError) IsTerminal() {} + +// NewTerminalError creates a new terminal error that cannot be retried +func NewTerminalError(err error) error { + return &terminalError{Err: err} +} + +// IsTerminalError returns true if the error is a terminal error +func IsTerminalError(err error) bool { + var te TerminalError + return errors.As(err, &te) +} diff --git a/internal/retry/error_test.go b/internal/retry/error_test.go new file mode 100644 index 00000000000..b4f2ddb00eb --- /dev/null +++ b/internal/retry/error_test.go @@ -0,0 +1,29 @@ +package retry_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/retry" +) + +type testError string + +func (e testError) Error() string { + return string(e) +} + +func (e testError) IsTerminal() {} + +func TestError(t *testing.T) { + t.Run("local terminal error", func(t *testing.T) { + err := fmt.Errorf("wrap: %w", retry.NewTerminalError(fmt.Errorf("inner"))) + require.True(t, retry.IsTerminalError(err)) + }) + t.Run("external terminal error", func(t *testing.T) { + err := fmt.Errorf("wrap: %w", testError("inner")) + require.True(t, retry.IsTerminalError(err)) + }) +} diff --git a/internal/retry/retry.go b/internal/retry/retry.go new file mode 100644 index 00000000000..02042e349c9 --- /dev/null +++ b/internal/retry/retry.go @@ -0,0 +1,139 @@ +// Package retry provides a retry loop with exponential back-off +// while watching arbitrary signal channels for side effects. +package retry + +import ( + "context" + "fmt" + "reflect" + "time" +) + +// Retry retries a function (with exponential back-off) until it succeeds. +// It additionally watches arbitrary channels and calls the handler function when a value is received. +// Handler functions are also retried with exponential back-off. +// If a terminal error is returned from the handler function, the retry loop is aborted. +// If the context is canceled, the retry loop is aborted. +func Retry( + ctx context.Context, + name string, + fn func(context.Context) error, + opts ...Option, +) error { + watches, backoff := newConfig(opts...) + ticker := time.NewTicker(backoff.NextBackOff()) + defer ticker.Stop() + + s := makeSelect(ctx, watches, name, ticker.C, fn) + +restart: + for { + err := fn(ctx) + if err == nil { + return nil + } + if IsTerminalError(err) { + return err + } + + backoff.Reset() + backoff: + for { + ticker.Reset(backoff.NextBackOff()) + + next, err := s.Exec(ctx) + switch next { + case nextRestart: + continue restart + case nextBackoff: + continue backoff + case nextExit: + return err + default: + panic("unreachable") + } + } + } +} + +type selectCase struct { + watches []watch + cases []reflect.SelectCase +} + +func makeSelect( + ctx context.Context, + watches []watch, + name string, + ch <-chan time.Time, + fn func(context.Context) error, +) *selectCase { + watches = append(watches, + watch{ + name: "context", + fn: func(ctx context.Context) error { + // unreachable, the context handler will never be called + // as its channel can only be closed + return ctx.Err() + }, + ch: reflect.ValueOf(ctx.Done()), + }, + watch{ + name: name, + fn: fn, + ch: reflect.ValueOf(ch), + this: true, + }, + ) + cases := make([]reflect.SelectCase, 0, len(watches)) + for _, w := range watches { + cases = append(cases, reflect.SelectCase{ + Dir: reflect.SelectRecv, + Chan: w.ch, + }) + } + return &selectCase{ + watches: watches, + cases: cases, + } +} + +type next int + +const ( + nextRestart next = iota // try again from the beginning + nextBackoff // backoff and try again + nextExit // exit +) + +func (s *selectCase) Exec(ctx context.Context) (next, error) { + chosen, _, ok := reflect.Select(s.cases) + if !ok { + return nextExit, fmt.Errorf("watch %s closed", s.watches[chosen].name) + } + + w := s.watches[chosen] + + err := w.fn(ctx) + if err != nil { + return onError(w, err) + } + + if !w.this { + return nextRestart, nil + } + + return nextExit, nil +} + +func onError(w watch, err error) (next, error) { + if IsTerminalError(err) { + return nextExit, err + } + + if w.this { + return nextBackoff, fmt.Errorf("retry %s failed: %w", w.name, err) + } + + panic("unreachable, as watches are wrapped in retries and may only return terminal errors") +} diff --git a/internal/retry/retry_test.go b/internal/retry/retry_test.go new file mode 100644 index 00000000000..c131653e9a4 --- /dev/null +++ b/internal/retry/retry_test.go @@ -0,0 +1,124 @@ +package retry_test + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/retry" +) + +func TestRetry(t *testing.T) { + t.Parallel() + + ctx := context.Background() + limit := retry.WithMaxInterval(time.Second * 5) + + t.Run("no error", func(t *testing.T) { + t.Parallel() + + err := retry.Retry(ctx, "test", func(_ context.Context) error { + return nil + }, limit) + require.NoError(t, err) + }) + + t.Run("eventually succeeds", func(t *testing.T) { + t.Parallel() + i := 0 + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if i++; i > 2 { + return nil + } + return fmt.Errorf("transient %d", i) + }, limit) + require.NoError(t, err) + }) + + t.Run("eventually fails", func(t *testing.T) { + t.Parallel() + i := 0 + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if i++; i > 2 { + return retry.NewTerminalError(errors.New("the end")) + } + return fmt.Errorf("transient %d", i) + }) + require.Error(t, err) + }) + + t.Run("context canceled", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(ctx) + cancel() + err := retry.Retry(ctx, "test", func(_ context.Context) error { + return fmt.Errorf("retry") + }) + require.Error(t, err) + }) + + t.Run("context canceled after retry", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(ctx) + t.Cleanup(cancel) + err := retry.Retry(ctx, "test", func(_ context.Context) error { + cancel() + return fmt.Errorf("retry") + }) + require.Error(t, err) + }) + + t.Run("success after watch hook", func(t *testing.T) { + t.Parallel() + ch := make(chan struct{}, 1) + ch <- struct{}{} + ok := false + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if ok { + return nil + } + return fmt.Errorf("retry") + }, retry.WithWatch("watch", ch, func(_ context.Context) error { + ok = true + return nil + }), limit) + require.NoError(t, err) + }) + + t.Run("success after watch hook retried", func(t *testing.T) { + t.Parallel() + ch := make(chan struct{}, 1) + ch <- struct{}{} + ok := false + i := 0 + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if ok { + return nil + } + return fmt.Errorf("retry test") + }, retry.WithWatch("watch", ch, func(_ context.Context) error { + if i++; i > 1 { + ok = true + return nil + } + return fmt.Errorf("retry watch") + }), limit) + require.NoError(t, err) + }) + + t.Run("watch hook fails", func(t *testing.T) { + t.Parallel() + ch := make(chan struct{}, 1) + ch <- struct{}{} + err := retry.Retry(ctx, "test", func(_ context.Context) error { + return fmt.Errorf("retry") + }, retry.WithWatch("watch", ch, func(_ context.Context) error { + return retry.NewTerminalError(fmt.Errorf("watch")) + }), limit) + require.Error(t, err) + }) +} From c94674a111a813d7ccf5363ed7d0cc5c9e297e2b Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Wed, 16 Aug 2023 12:47:46 -0400 Subject: [PATCH 2/9] zero: bootstrap config (#4444) --- go.mod | 64 +++++++++- go.sum | 178 ++++++++++++++++++++++++--- internal/zero/bootstrap/bootstrap.go | 136 ++++++++++++++++++++ internal/zero/bootstrap/file.go | 54 ++++++++ internal/zero/bootstrap/file_test.go | 33 +++++ internal/zero/bootstrap/new.go | 126 +++++++++++++++++++ internal/zero/bootstrap/new_test.go | 30 +++++ internal/zero/bootstrap/source.go | 93 ++++++++++++++ 8 files changed, 694 insertions(+), 20 deletions(-) create mode 100644 internal/zero/bootstrap/bootstrap.go create mode 100644 internal/zero/bootstrap/file.go create mode 100644 internal/zero/bootstrap/file_test.go create mode 100644 internal/zero/bootstrap/new.go create mode 100644 internal/zero/bootstrap/new_test.go create mode 100644 internal/zero/bootstrap/source.go diff --git a/go.mod b/go.mod index 721efcc8e02..b2b35f8946b 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( github.com/pomerium/csrf v1.7.0 github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 github.com/pomerium/webauthn v0.0.0-20221118023040-00a9c430578b + github.com/pomerium/zero-sdk v0.0.0-20230816163741-2c7886877a34 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 github.com/prometheus/common v0.44.0 @@ -64,7 +65,7 @@ require ( go.opencensus.io v0.24.0 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 golang.org/x/net v0.17.0 golang.org/x/oauth2 v0.12.0 golang.org/x/sync v0.3.0 @@ -82,13 +83,14 @@ require ( cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.1 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/DataDog/datadog-go v3.5.0+incompatible // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.40 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect @@ -105,31 +107,55 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect github.com/aws/smithy-go v1.14.2 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bufbuild/buf v1.25.0 // indirect + github.com/bufbuild/connect-go v1.9.0 // indirect + github.com/bufbuild/connect-opentelemetry-go v0.4.0 // indirect + github.com/bufbuild/protocompile v0.5.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect github.com/containerd/continuity v0.4.2 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deepmap/oapi-codegen v1.13.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/docker/cli v23.0.3+incompatible // indirect + github.com/docker/cli v24.0.4+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.0 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/felixge/fgprof v0.9.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/getkin/kin-openapi v0.118.0 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/swag v0.21.1 // 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.14.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-containerregistry v0.15.2 // indirect github.com/google/go-tpm v0.3.3 // indirect + github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect @@ -138,49 +164,71 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/yaml v0.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/pgzip v1.2.6 // indirect + github.com/labstack/echo/v4 v4.11.1 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.55 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc4 // indirect github.com/opencontainers/runc v1.1.5 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/philhofer/fwd v1.0.0 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/profile v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/xid v1.5.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/tetratelabs/wazero v1.3.0 // indirect github.com/tinylib/msgp v1.1.2 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/vbatts/tar-split v0.11.3 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -192,16 +240,20 @@ require ( go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/sdk v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.11.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.10.0 // indirect + golang.org/x/tools v0.11.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect gopkg.in/DataDog/dd-trace-go.v1 v1.22.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect diff --git a/go.sum b/go.sum index ba3c855b591..3cdb66c1cfc 100644 --- a/go.sum +++ b/go.sum @@ -52,9 +52,10 @@ contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9 contrib.go.opencensus.io/exporter/zipkin v0.1.2 h1:YqE293IZrKtqPnpwDPH/lOqTWD/s3Iwabycam74JV3g= contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj3gsxcuytSqtH0dxSWW1RE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg= github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM= @@ -69,6 +70,7 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= @@ -85,6 +87,8 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -128,8 +132,20 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/bufbuild/buf v1.25.0 h1:HFxKrR8wFcZwrBInN50K/oJX/WOtPVq24rHb/ArjfBA= +github.com/bufbuild/buf v1.25.0/go.mod h1:GCKZ5bAP6Ht4MF7KcfaGVgBEXGumwAz2hXjjLVxx8ZU= +github.com/bufbuild/connect-go v1.9.0 h1:JIgAeNuFpo+SUPfU19Yt5TcWlznsN5Bv10/gI/6Pjoc= +github.com/bufbuild/connect-go v1.9.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8= +github.com/bufbuild/connect-opentelemetry-go v0.4.0 h1:6JAn10SNqlQ/URhvRNGrIlczKw1wEXknBUUtmWqOiak= +github.com/bufbuild/connect-opentelemetry-go v0.4.0/go.mod h1:nwPXYoDOoc2DGyKE/6pT1Q9MPSi2Et2e6BieMD0l6WU= +github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg= +github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY8+wjsYLR/lc40= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0= github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -144,6 +160,9 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL 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/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -159,6 +178,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWH github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= +github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -172,12 +193,17 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 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/deepmap/oapi-codegen v1.13.2 h1:I/REY+90rmcheXdR5rCg9j+7wAI134UwBk44AYt5QAY= +github.com/deepmap/oapi-codegen v1.13.2/go.mod h1:eXAuxgJu9XC+dZECAw9cF4qtmL0qwGy0ZAvV6A2j1oA= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= @@ -188,12 +214,14 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/docker/cli v23.0.3+incompatible h1:Zcse1DuDqBdgI7OQDV8Go7b83xLgfhW1eza4HfEdxpY= -github.com/docker/cli v23.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v24.0.4+incompatible h1:Y3bYF9ekNTm2VFz5U/0BlMdJy73D+Y1iAAZ8l63Ydzw= +github.com/docker/cli v24.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= +github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -215,6 +243,8 @@ github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -230,7 +260,15 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= +github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -258,15 +296,36 @@ 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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +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= +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-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= +github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -312,6 +371,8 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k= github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -335,6 +396,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= +github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= @@ -360,6 +423,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= @@ -406,9 +472,14 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -417,9 +488,15 @@ github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 h1:2uT3aivO7NVpUPGcQX7RbHijHMyWix/yCnIrCWc+5co= +github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84/go.mod h1:Zi/ZFkEqFHTm7qkjyNJjaWH4LQA9LQhGJyF0lTYGpxw= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -428,6 +505,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -437,9 +515,11 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -453,6 +533,12 @@ 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/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= +github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= @@ -463,10 +549,17 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg= github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -483,6 +576,7 @@ github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurw github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= @@ -490,8 +584,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh 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/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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= @@ -499,6 +593,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= @@ -506,6 +602,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -534,6 +631,8 @@ github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnz github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= @@ -542,11 +641,15 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1 h1:VGcrWe3yk6o+t7BdV github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -556,6 +659,8 @@ github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 h1:3YQY1sb5 github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524/go.mod h1:7fGbUYJnU8RcxZJvUvhukOIBv1G7LWDAHMfDxAf5+Y0= github.com/pomerium/webauthn v0.0.0-20221118023040-00a9c430578b h1:oll/aOfJudnqFAwCvoXK9+WN2zVjTzHVPLXCggHQmHk= github.com/pomerium/webauthn v0.0.0-20221118023040-00a9c430578b/go.mod h1:KswTenBBh4y1pmhU2dpm8VgJQCgSErCg7OOFTeebrNc= +github.com/pomerium/zero-sdk v0.0.0-20230816163741-2c7886877a34 h1:UMt+h1nKW9DDCYzejkGaYFjKzOHFgIP9zorRMADbKhM= +github.com/pomerium/zero-sdk v0.0.0-20230816163741-2c7886877a34/go.mod h1:cAyfEGM8blUzchYhOWrufuj/6lOF277meB4c/TjMS28= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -601,8 +706,8 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= @@ -611,6 +716,8 @@ github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -625,6 +732,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -637,6 +745,8 @@ github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -647,6 +757,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -662,6 +773,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -671,6 +783,8 @@ github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNG github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/tetratelabs/wazero v1.3.0 h1:nqw7zCldxE06B8zSZAY0ACrR9OH5QCcPwYmYlwtcwtE= +github.com/tetratelabs/wazero v1.3.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -680,14 +794,28 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f h1:C43EMGXFtvYf/zunHR6ivZV7Z6ytg73t0GXwYyicXMQ= github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f/go.mod h1:N+sR0vLSCTtI6o06PMWsjMB4TVqqDttKNq4iC9wvxVY= +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/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g= github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ= +github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= +github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/volatiletech/null/v9 v9.0.0 h1:JCdlHEiSRVxOi7/MABiEfdsqmuj9oTV20Ao7VvZ0JkE= @@ -744,11 +872,15 @@ go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26 go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -757,6 +889,9 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -779,8 +914,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= -golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -805,8 +940,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -931,10 +1066,15 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -943,6 +1083,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -953,6 +1094,8 @@ golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1019,8 +1162,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= 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= @@ -1118,6 +1261,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1141,6 +1286,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -1162,6 +1309,8 @@ gopkg.in/yaml.v2 v2.3.0/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.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/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= @@ -1177,6 +1326,7 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 namespacelabs.dev/go-filenotify v0.0.0-20220511192020-53ea11be7eaa h1:jj2kjs0Hvufj40wuhMAzoZUOwrwMDFg1gHZ49RiIv9w= namespacelabs.dev/go-filenotify v0.0.0-20220511192020-53ea11be7eaa/go.mod h1:e8NJRaInXRRm1+KPA6EkGEzdLJAgEvVSIKiLzpP97nI= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/internal/zero/bootstrap/bootstrap.go b/internal/zero/bootstrap/bootstrap.go new file mode 100644 index 00000000000..6cb176c7058 --- /dev/null +++ b/internal/zero/bootstrap/bootstrap.go @@ -0,0 +1,136 @@ +// Package bootstrap fetches the very initial configuration for Pomerium Core to start. +package bootstrap + +/* + * Initial configuration for Pomerium start-up is obtained from the cloud. + * Some parameters are derived from the cluster token. + * + * The expectation is that if the user wishes to survive a cloud outage, + * it should be sufficient to set up Pomerium to use a durable database (Postgres) + * and receive cloud configuration once. + * + */ + +import ( + "context" + "fmt" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/retry" + sdk "github.com/pomerium/zero-sdk" + connect_mux "github.com/pomerium/zero-sdk/connect-mux" +) + +const ( + // DefaultCheckForUpdateIntervalWhenDisconnected is the default interval to check for updates + // if there is no connection to the update service + DefaultCheckForUpdateIntervalWhenDisconnected = 5 * time.Minute + // DefaultCheckForUpdateIntervalWhenConnected is the default interval to check for updates + // if there is a connection to the update service + DefaultCheckForUpdateIntervalWhenConnected = time.Hour +) + +// Run initializes the bootstrap config source +func (svc *Source) Run( + ctx context.Context, + api *sdk.API, + fileCachePath string, +) error { + svc.api = api + svc.fileCachePath = fileCachePath + + svc.tryLoadInitial(ctx) + + eg, ctx := errgroup.WithContext(ctx) + eg.Go(func() error { return svc.watchUpdates(ctx) }) + eg.Go(func() error { return svc.updateLoop(ctx) }) + + return eg.Wait() +} + +func (svc *Source) watchUpdates(ctx context.Context) error { + return svc.api.Watch(ctx, + connect_mux.WithOnConnected(func(_ context.Context) { + svc.triggerUpdate(DefaultCheckForUpdateIntervalWhenConnected) + }), + connect_mux.WithOnDisconnected(func(_ context.Context) { + svc.updateInterval.Store(DefaultCheckForUpdateIntervalWhenDisconnected) + }), + connect_mux.WithOnBootstrapConfigUpdated(func(_ context.Context) { + svc.triggerUpdate(DefaultCheckForUpdateIntervalWhenConnected) + }), + ) +} + +func (svc *Source) updateLoop(ctx context.Context) error { + ticker := time.NewTicker(svc.updateInterval.Load()) + defer ticker.Stop() + + for { + ticker.Reset(svc.updateInterval.Load()) + + select { + case <-ctx.Done(): + return ctx.Err() + case <-svc.checkForUpdate: + case <-ticker.C: + } + + err := retry.Retry(ctx, + "update bootstrap", svc.updateAndSave, + retry.WithWatch("bootstrap config updated", svc.checkForUpdate, nil), + ) + if err != nil { + return fmt.Errorf("update bootstrap config: %w", err) + } + } +} + +// triggerUpdate triggers an update of the bootstrap config +// and sets the interval for the next update +func (svc *Source) triggerUpdate(newUpdateInterval time.Duration) { + svc.updateInterval.Store(newUpdateInterval) + + select { + case svc.checkForUpdate <- struct{}{}: + default: + } +} + +func (svc *Source) updateAndSave(ctx context.Context) error { + cfg, err := svc.api.GetClusterBootstrapConfig(ctx) + if err != nil { + return fmt.Errorf("load bootstrap config from API: %w", err) + } + + err = SaveBootstrapConfigToFile(cfg, svc.fileCachePath, svc.fileCipher) + if err != nil { + log.Ctx(ctx).Error().Err(err). + Msg("failed to save bootstrap config to file, note it may prevent Pomerium from starting up in case of connectivity issues") + } + + svc.UpdateBootstrap(ctx, *cfg) + return nil +} + +func (svc *Source) tryLoadInitial(ctx context.Context) { + err := svc.updateAndSave(ctx) + if err != nil { + log.Ctx(ctx).Error().Err(err).Msg("failed to load bootstrap config") + svc.tryLoadFromFile(ctx) + return + } +} + +func (svc *Source) tryLoadFromFile(ctx context.Context) { + cfg, err := LoadBootstrapConfigFromFile(svc.fileCachePath, svc.fileCipher) + if err != nil { + log.Ctx(ctx).Error().Err(err).Msg("failed to load bootstrap config from file") + return + } + + svc.UpdateBootstrap(ctx, *cfg) +} diff --git a/internal/zero/bootstrap/file.go b/internal/zero/bootstrap/file.go new file mode 100644 index 00000000000..8b248a2aec1 --- /dev/null +++ b/internal/zero/bootstrap/file.go @@ -0,0 +1,54 @@ +package bootstrap + +/* + * in order to be able to start up pomerium in case cloud is unreachable, + * we store the minimum bootstrap configuration (essentially, the data broker connection) + * in a file. this file is encrypted with a key that is derived from the cluster token. + * + * this information should be sufficient for pomerium to locate the database and start up. + * + */ +import ( + "crypto/cipher" + "encoding/json" + "fmt" + "os" + + "github.com/pomerium/pomerium/pkg/cryptutil" + cluster_api "github.com/pomerium/zero-sdk/cluster" +) + +// LoadBootstrapConfigFromFile loads the bootstrap configuration from a file. +func LoadBootstrapConfigFromFile(fp string, cipher cipher.AEAD) (*cluster_api.BootstrapConfig, error) { + ciphertext, err := os.ReadFile(fp) + if err != nil { + return nil, fmt.Errorf("read bootstrap config: %w", err) + } + plaintext, err := cryptutil.Decrypt(cipher, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("decrypt bootstrap config: %w", err) + } + + var dst cluster_api.BootstrapConfig + err = json.Unmarshal(plaintext, &dst) + if err != nil { + return nil, fmt.Errorf("unmarshal bootstrap config: %w", err) + } + + return &dst, nil +} + +// SaveBootstrapConfigToFile saves the bootstrap configuration to a file. +func SaveBootstrapConfigToFile(src *cluster_api.BootstrapConfig, fp string, cipher cipher.AEAD) error { + plaintext, err := json.Marshal(src) + if err != nil { + return fmt.Errorf("marshal file config: %w", err) + } + + ciphertext := cryptutil.Encrypt(cipher, plaintext, nil) + err = os.WriteFile(fp, ciphertext, 0600) + if err != nil { + return fmt.Errorf("write bootstrap config: %w", err) + } + return nil +} diff --git a/internal/zero/bootstrap/file_test.go b/internal/zero/bootstrap/file_test.go new file mode 100644 index 00000000000..93aa410e8d6 --- /dev/null +++ b/internal/zero/bootstrap/file_test.go @@ -0,0 +1,33 @@ +package bootstrap_test + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/zero/bootstrap" + "github.com/pomerium/pomerium/pkg/cryptutil" + cluster_api "github.com/pomerium/zero-sdk/cluster" +) + +func TestFile(t *testing.T) { + cipher, err := cryptutil.NewAEADCipher(cryptutil.NewKey()) + require.NoError(t, err) + + txt := "test" + src := cluster_api.BootstrapConfig{ + DatabrokerStorageConnection: &txt, + } + + fd, err := os.CreateTemp(t.TempDir(), "test.data") + require.NoError(t, err) + require.NoError(t, fd.Close()) + + require.NoError(t, bootstrap.SaveBootstrapConfigToFile(&src, fd.Name(), cipher)) + + dst, err := bootstrap.LoadBootstrapConfigFromFile(fd.Name(), cipher) + require.NoError(t, err) + + require.Equal(t, src, *dst) +} diff --git a/internal/zero/bootstrap/new.go b/internal/zero/bootstrap/new.go new file mode 100644 index 00000000000..2bafb577db5 --- /dev/null +++ b/internal/zero/bootstrap/new.go @@ -0,0 +1,126 @@ +package bootstrap + +import ( + "crypto/cipher" + "crypto/elliptic" + "crypto/sha256" + "encoding/base64" + "fmt" + "io" + "time" + + "golang.org/x/crypto/hkdf" + + "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/internal/atomicutil" + "github.com/pomerium/pomerium/internal/deterministicecdsa" + "github.com/pomerium/pomerium/pkg/cryptutil" + "github.com/pomerium/pomerium/pkg/netutil" + sdk "github.com/pomerium/zero-sdk" +) + +// Source is a base config layer for Pomerium +type Source struct { + source + + api *sdk.API + + fileCachePath string + fileCipher cipher.AEAD + + checkForUpdate chan struct{} + updateInterval atomicutil.Value[time.Duration] +} + +// New creates a new bootstrap config source +func New(secret []byte) (*Source, error) { + cfg := new(config.Config) + + err := setConfigDefaults(cfg) + if err != nil { + return nil, fmt.Errorf("config defaults: %w", err) + } + + rnd := hkdf.New(sha256.New, secret, nil, nil) + + cipher, err := initCipher(rnd) + if err != nil { + return nil, fmt.Errorf("init cypher: %w", err) + } + + err = initSecrets(cfg, rnd) + if err != nil { + return nil, fmt.Errorf("init secrets: %w", err) + } + + svc := &Source{ + source: source{ready: make(chan struct{})}, + fileCipher: cipher, + checkForUpdate: make(chan struct{}, 1), + } + svc.cfg.Store(cfg) + svc.updateInterval.Store(DefaultCheckForUpdateIntervalWhenDisconnected) + + return svc, nil +} + +func setConfigDefaults(cfg *config.Config) error { + cfg.Options = config.NewDefaultOptions() + + ports, err := netutil.AllocatePorts(6) + if err != nil { + return fmt.Errorf("allocating ports: %w", err) + } + + cfg.AllocatePorts(*(*[6]string)(ports[:6])) + + return nil +} + +func readKey(r io.Reader) ([]byte, error) { + b := make([]byte, cryptutil.DefaultKeySize) + _, err := io.ReadFull(r, b) + if err != nil { + return nil, fmt.Errorf("read from hkdf: %w", err) + } + return b, nil +} + +func initCipher(r io.Reader) (cipher.AEAD, error) { + cipherKey, err := readKey(r) + if err != nil { + return nil, fmt.Errorf("read key: %w", err) + } + cipher, err := cryptutil.NewAEADCipher(cipherKey) + if err != nil { + return nil, fmt.Errorf("new aead cipher: %w", err) + } + return cipher, nil +} + +func initSecrets(cfg *config.Config, r io.Reader) error { + signingKey, err := deterministicecdsa.GenerateKey(elliptic.P256(), r) + if err != nil { + return fmt.Errorf("read key: %w", err) + } + signingKeyEncoded, err := cryptutil.EncodePrivateKey(signingKey) + if err != nil { + return fmt.Errorf("pem: %w", err) + } + + sharedKey, err := readKey(r) + if err != nil { + return fmt.Errorf("read key: %w", err) + } + + cookieSecret, err := readKey(r) + if err != nil { + return fmt.Errorf("read key: %w", err) + } + + cfg.Options.SharedKey = base64.StdEncoding.EncodeToString(sharedKey) + cfg.Options.CookieSecret = base64.StdEncoding.EncodeToString(cookieSecret) + cfg.Options.SigningKey = base64.StdEncoding.EncodeToString(signingKeyEncoded) + + return nil +} diff --git a/internal/zero/bootstrap/new_test.go b/internal/zero/bootstrap/new_test.go new file mode 100644 index 00000000000..8a2a755174b --- /dev/null +++ b/internal/zero/bootstrap/new_test.go @@ -0,0 +1,30 @@ +package bootstrap_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/zero/bootstrap" +) + +func TestConfigDeterministic(t *testing.T) { + secret := []byte("secret") + + src, err := bootstrap.New(secret) + require.NoError(t, err) + cfg := src.GetConfig() + require.NotNil(t, cfg) + + // test that the config is valid + require.NoError(t, cfg.Options.Validate()) + + // test that the config is deterministic + src2, err := bootstrap.New(secret) + require.NoError(t, err) + + cfg2 := src2.GetConfig() + require.NotNil(t, cfg2) + + require.Equal(t, cfg.Options, cfg2.Options) +} diff --git a/internal/zero/bootstrap/source.go b/internal/zero/bootstrap/source.go new file mode 100644 index 00000000000..f9831d02ca0 --- /dev/null +++ b/internal/zero/bootstrap/source.go @@ -0,0 +1,93 @@ +package bootstrap + +import ( + "context" + "sync" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/internal/atomicutil" + cluster_api "github.com/pomerium/zero-sdk/cluster" +) + +var ( + _ = config.Source(new(source)) +) + +var ( + cmpOpts = []cmp.Option{ + cmpopts.IgnoreUnexported(config.Options{}), + cmpopts.EquateEmpty(), + } +) + +type source struct { + cfg atomicutil.Value[*config.Config] + + listenerLock sync.RWMutex + listeners []config.ChangeListener + + ready chan struct{} + markReady sync.Once +} + +func (src *source) WaitReady(ctx context.Context) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-src.ready: + return nil + } +} + +// GetConfig implements config.Source +func (src *source) GetConfig() *config.Config { + return src.cfg.Load() +} + +// OnConfigChange implements config.Source +func (src *source) OnConfigChange(_ context.Context, l config.ChangeListener) { + src.listenerLock.Lock() + src.listeners = append(src.listeners, l) + src.listenerLock.Unlock() +} + +// UpdateBootstrap updates the underlying configuration options +func (src *source) UpdateBootstrap(ctx context.Context, cfg cluster_api.BootstrapConfig) bool { + current := src.cfg.Load() + incoming := current.Clone() + applyBootstrapConfig(incoming.Options, &cfg) + + src.markReady.Do(func() { close(src.ready) }) + + if cmp.Equal(incoming.Options, current.Options, cmpOpts...) { + return false + } + + src.cfg.Store(incoming) + + src.notifyListeners(ctx, incoming) + + return true +} + +// notifyListeners notifies all listeners of a configuration change +func (src *source) notifyListeners(ctx context.Context, cfg *config.Config) { + src.listenerLock.RLock() + listeners := make([]config.ChangeListener, len(src.listeners)) + copy(listeners, src.listeners) + src.listenerLock.RUnlock() + + for _, l := range listeners { + l(ctx, cfg) + } +} + +func applyBootstrapConfig(dst *config.Options, src *cluster_api.BootstrapConfig) { + if src.DatabrokerStorageConnection != nil { + dst.DataBrokerStorageType = "postgres" + dst.DataBrokerStorageConnectionString = *src.DatabrokerStorageConnection + } +} From 21b5cf90891d503ea8114f19c1ba234e9f4915ef Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Thu, 17 Aug 2023 13:19:51 -0400 Subject: [PATCH 3/9] zero: resource bundle reconciler (#4445) --- go.mod | 1 + internal/zero/reconciler/bundles_format.go | 35 +++ .../zero/reconciler/bundles_format_test.go | 64 +++++ internal/zero/reconciler/bundles_queue.go | 124 ++++++++++ .../zero/reconciler/bundles_queue_test.go | 95 +++++++ internal/zero/reconciler/config.go | 110 +++++++++ internal/zero/reconciler/databroker.go | 81 ++++++ .../zero/reconciler/databroker_changeset.go | 53 ++++ internal/zero/reconciler/download_cache.go | 168 +++++++++++++ .../zero/reconciler/download_cache_test.go | 29 +++ internal/zero/reconciler/reconciler.go | 34 +++ internal/zero/reconciler/reconciler_test.go | 196 +++++++++++++++ internal/zero/reconciler/records.go | 132 ++++++++++ internal/zero/reconciler/records_test.go | 77 ++++++ internal/zero/reconciler/service.go | 89 +++++++ internal/zero/reconciler/sync.go | 231 ++++++++++++++++++ internal/zero/reconciler/tmpfile.go | 40 +++ 17 files changed, 1559 insertions(+) create mode 100644 internal/zero/reconciler/bundles_format.go create mode 100644 internal/zero/reconciler/bundles_format_test.go create mode 100644 internal/zero/reconciler/bundles_queue.go create mode 100644 internal/zero/reconciler/bundles_queue_test.go create mode 100644 internal/zero/reconciler/config.go create mode 100644 internal/zero/reconciler/databroker.go create mode 100644 internal/zero/reconciler/databroker_changeset.go create mode 100644 internal/zero/reconciler/download_cache.go create mode 100644 internal/zero/reconciler/download_cache_test.go create mode 100644 internal/zero/reconciler/reconciler.go create mode 100644 internal/zero/reconciler/reconciler_test.go create mode 100644 internal/zero/reconciler/records.go create mode 100644 internal/zero/reconciler/records_test.go create mode 100644 internal/zero/reconciler/service.go create mode 100644 internal/zero/reconciler/sync.go create mode 100644 internal/zero/reconciler/tmpfile.go diff --git a/go.mod b/go.mod index b2b35f8946b..d366f10577f 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( golang.org/x/net v0.17.0 golang.org/x/oauth2 v0.12.0 golang.org/x/sync v0.3.0 + golang.org/x/time v0.3.0 google.golang.org/api v0.143.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 google.golang.org/grpc v1.58.3 diff --git a/internal/zero/reconciler/bundles_format.go b/internal/zero/reconciler/bundles_format.go new file mode 100644 index 00000000000..001a806f6d5 --- /dev/null +++ b/internal/zero/reconciler/bundles_format.go @@ -0,0 +1,35 @@ +package reconciler + +import ( + "bufio" + "errors" + "fmt" + "io" + + "google.golang.org/protobuf/encoding/protodelim" + + "github.com/pomerium/pomerium/pkg/grpc/databroker" +) + +var unmarshalOpts = protodelim.UnmarshalOptions{} + +// ReadBundleRecords reads records in a protobuf wire format from src. +// Each record is expected to be a databroker.Record. +func ReadBundleRecords(src io.Reader) (RecordSetBundle[DatabrokerRecord], error) { + r := bufio.NewReader(src) + rsb := make(RecordSetBundle[DatabrokerRecord]) + for { + record := new(databroker.Record) + err := unmarshalOpts.UnmarshalFrom(r, record) + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, fmt.Errorf("error reading protobuf record: %w", err) + } + + rsb.Add(DatabrokerRecord{record}) + } + + return rsb, nil +} diff --git a/internal/zero/reconciler/bundles_format_test.go b/internal/zero/reconciler/bundles_format_test.go new file mode 100644 index 00000000000..0af272a6344 --- /dev/null +++ b/internal/zero/reconciler/bundles_format_test.go @@ -0,0 +1,64 @@ +package reconciler + +import ( + "fmt" + "io" + "os" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protodelim" + "google.golang.org/protobuf/proto" + + "github.com/pomerium/pomerium/pkg/grpc/config" + "github.com/pomerium/pomerium/pkg/grpc/databroker" + "github.com/pomerium/pomerium/pkg/protoutil" +) + +func TestReadRecords(t *testing.T) { + + dir := t.TempDir() + fd, err := os.CreateTemp(dir, "config") + require.NoError(t, err) + t.Cleanup(func() { _ = fd.Close() }) + + err = writeSampleRecords(fd) + require.NoError(t, err) + + _, err = fd.Seek(0, io.SeekStart) + require.NoError(t, err) + + records, err := ReadBundleRecords(fd) + require.NoError(t, err) + require.Len(t, records, 1) +} + +func writeSampleRecords(dst io.Writer) error { + var marshalOpts = protodelim.MarshalOptions{ + MarshalOptions: proto.MarshalOptions{ + AllowPartial: false, + Deterministic: true, + UseCachedSize: false, + }, + } + + cfg := protoutil.NewAny(&config.Config{ + Routes: []*config.Route{ + { + From: "https://from.example.com", + To: []string{"https://to.example.com"}, + }, + }, + }) + rec := &databroker.Record{ + Id: "config", + Type: cfg.GetTypeUrl(), + Data: cfg, + } + _, err := marshalOpts.MarshalTo(dst, rec) + if err != nil { + return fmt.Errorf("marshal: %w", err) + } + + return nil +} diff --git a/internal/zero/reconciler/bundles_queue.go b/internal/zero/reconciler/bundles_queue.go new file mode 100644 index 00000000000..8ed3f6b7b96 --- /dev/null +++ b/internal/zero/reconciler/bundles_queue.go @@ -0,0 +1,124 @@ +package reconciler + +import ( + "container/heap" + "sync" +) + +type bundle struct { + id string + synced bool + priority int +} + +type bundleHeap []bundle + +func (h bundleHeap) Len() int { return len(h) } +func (h bundleHeap) Less(i, j int) bool { + // If one is synced and the other is not, the unsynced one comes first + if h[i].synced != h[j].synced { + return !h[i].synced + } + // Otherwise, the one with the lower priority comes first + return h[i].priority < h[j].priority +} + +func (h bundleHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *bundleHeap) Push(x interface{}) { + item := x.(bundle) + *h = append(*h, item) +} + +func (h *bundleHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// BundleQueue is a priority queue of bundles to sync. +type BundleQueue struct { + sync.Mutex + bundles bundleHeap + counter int // to assign priorities based on order of insertion +} + +// Set sets the bundles to be synced. This will reset the sync status of all bundles. +func (b *BundleQueue) Set(bundles []string) { + b.Lock() + defer b.Unlock() + + b.bundles = make(bundleHeap, len(bundles)) + b.counter = len(bundles) + for i, id := range bundles { + b.bundles[i] = bundle{ + id: id, + synced: false, + priority: i, + } + } + heap.Init(&b.bundles) +} + +// MarkForSync marks the bundle with the given ID for syncing. +func (b *BundleQueue) MarkForSync(id string) { + b.Lock() + defer b.Unlock() + + for i, bundle := range b.bundles { + if bundle.id == id { + b.bundles[i].synced = false + heap.Fix(&b.bundles, i) + return + } + } + + newBundle := bundle{id: id, synced: false, priority: b.counter} + heap.Push(&b.bundles, newBundle) + b.counter++ +} + +// MarkForSyncLater marks the bundle with the given ID for syncing later (after all other bundles). +func (b *BundleQueue) MarkForSyncLater(id string) { + b.Lock() + defer b.Unlock() + + for i, bundle := range b.bundles { + if bundle.id != id { + continue + } + + // Increase the counter first to ensure that this bundle has the highest (last) priority. + b.counter++ + b.bundles[i].synced = false + b.bundles[i].priority = b.counter + heap.Fix(&b.bundles, i) + return + } +} + +// GetNextBundleToSync returns the ID of the next bundle to sync and whether there is one. +func (b *BundleQueue) GetNextBundleToSync() (string, bool) { + b.Lock() + defer b.Unlock() + + if len(b.bundles) == 0 { + return "", false + } + + // Check the top bundle without popping + if b.bundles[0].synced { + return "", false + } + + // Mark the top bundle as synced and push it to the end + id := b.bundles[0].id + b.bundles[0].synced = true + b.bundles[0].priority = b.counter + heap.Fix(&b.bundles, 0) + b.counter++ + + return id, true +} diff --git a/internal/zero/reconciler/bundles_queue_test.go b/internal/zero/reconciler/bundles_queue_test.go new file mode 100644 index 00000000000..dd219029280 --- /dev/null +++ b/internal/zero/reconciler/bundles_queue_test.go @@ -0,0 +1,95 @@ +package reconciler_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/zero/reconciler" +) + +func TestQueueSet(t *testing.T) { + t.Parallel() + + b := &reconciler.BundleQueue{} + b.Set([]string{"bundle1", "bundle2"}) + + id1, ok1 := b.GetNextBundleToSync() + id2, ok2 := b.GetNextBundleToSync() + + assert.True(t, ok1, "Expected bundle1 to be set") + assert.Equal(t, "bundle1", id1) + assert.True(t, ok2, "Expected bundle2 to be set") + assert.Equal(t, "bundle2", id2) + + id3, ok3 := b.GetNextBundleToSync() + assert.False(t, ok3, "Expected no more bundles to sync") + assert.Empty(t, id3) +} + +func TestQueueMarkForSync(t *testing.T) { + t.Parallel() + + b := &reconciler.BundleQueue{} + b.Set([]string{"bundle1", "bundle2"}) + + b.MarkForSync("bundle2") + id1, ok1 := b.GetNextBundleToSync() + + assert.True(t, ok1, "Expected bundle1 to be marked for sync") + assert.Equal(t, "bundle1", id1) + + b.MarkForSync("bundle3") + id2, ok2 := b.GetNextBundleToSync() + id3, ok3 := b.GetNextBundleToSync() + + assert.True(t, ok2, "Expected bundle2 to be marked for sync") + assert.Equal(t, "bundle2", id2) + assert.True(t, ok3, "Expected bundle3 to be marked for sync") + assert.Equal(t, "bundle3", id3) +} + +func TestQueueMarkForSyncLater(t *testing.T) { + t.Parallel() + + b := &reconciler.BundleQueue{} + b.Set([]string{"bundle1", "bundle2", "bundle3"}) + + id1, ok1 := b.GetNextBundleToSync() + b.MarkForSyncLater("bundle1") + id2, ok2 := b.GetNextBundleToSync() + id3, ok3 := b.GetNextBundleToSync() + id4, ok4 := b.GetNextBundleToSync() + id5, ok5 := b.GetNextBundleToSync() + + assert.True(t, ok1, "Expected bundle1 to be marked for sync") + assert.Equal(t, "bundle1", id1) + assert.True(t, ok2, "Expected bundle2 to be marked for sync") + assert.Equal(t, "bundle2", id2) + assert.True(t, ok3, "Expected bundle3 to be marked for sync") + assert.Equal(t, "bundle3", id3) + assert.True(t, ok4, "Expected bundle1 to be marked for sync") + assert.Equal(t, "bundle1", id4) + assert.False(t, ok5, "Expected no more bundles to sync") + assert.Empty(t, id5) + +} + +func TestQueueGetNextBundleToSync(t *testing.T) { + t.Parallel() + + b := &reconciler.BundleQueue{} + b.Set([]string{"bundle1", "bundle2"}) + + id1, ok1 := b.GetNextBundleToSync() + id2, ok2 := b.GetNextBundleToSync() + id3, ok3 := b.GetNextBundleToSync() + + assert.True(t, ok1, "Expected bundle1 to be retrieved for sync") + assert.Equal(t, "bundle1", id1) + assert.True(t, ok2, "Expected bundle2 to be retrieved for sync") + assert.Equal(t, "bundle2", id2) + require.False(t, ok3, "Expected no more bundles to sync") + assert.Empty(t, id3) +} diff --git a/internal/zero/reconciler/config.go b/internal/zero/reconciler/config.go new file mode 100644 index 00000000000..d9f48546fb6 --- /dev/null +++ b/internal/zero/reconciler/config.go @@ -0,0 +1,110 @@ +// Package reconciler syncs the state of resource bundles between the cloud and the databroker. +package reconciler + +import ( + "net/http" + "os" + "time" + + "github.com/pomerium/pomerium/pkg/grpc/databroker" + sdk "github.com/pomerium/zero-sdk" +) + +// reconcilerConfig contains the configuration for the resource bundles reconciler. +type reconcilerConfig struct { + api *sdk.API + + databrokerClient databroker.DataBrokerServiceClient + databrokerRPS int + + tmpDir string + + httpClient *http.Client + + checkForUpdateIntervalWhenDisconnected time.Duration + checkForUpdateIntervalWhenConnected time.Duration + + syncBackoffMaxInterval time.Duration +} + +// Option configures the resource bundles reconciler +type Option func(*reconcilerConfig) + +// WithTemporaryDirectory configures the resource bundles client to use a temporary directory for +// downloading files. +func WithTemporaryDirectory(path string) Option { + return func(cfg *reconcilerConfig) { + cfg.tmpDir = path + } +} + +// WithAPI configures the cluster api client. +func WithAPI(client *sdk.API) Option { + return func(cfg *reconcilerConfig) { + cfg.api = client + } +} + +// WithDataBrokerClient configures the databroker client. +func WithDataBrokerClient(client databroker.DataBrokerServiceClient) Option { + return func(cfg *reconcilerConfig) { + cfg.databrokerClient = client + } +} + +// WithDownloadHTTPClient configures the http client used for downloading files. +func WithDownloadHTTPClient(client *http.Client) Option { + return func(cfg *reconcilerConfig) { + cfg.httpClient = client + } +} + +// WithDatabrokerRPSLimit configures the maximum number of requests per second to the databroker. +func WithDatabrokerRPSLimit(rps int) Option { + return func(cfg *reconcilerConfig) { + cfg.databrokerRPS = rps + } +} + +// WithCheckForUpdateIntervalWhenDisconnected configures the interval at which the reconciler will check +// for updates when disconnected from the cloud. +func WithCheckForUpdateIntervalWhenDisconnected(interval time.Duration) Option { + return func(cfg *reconcilerConfig) { + cfg.checkForUpdateIntervalWhenDisconnected = interval + } +} + +// WithCheckForUpdateIntervalWhenConnected configures the interval at which the reconciler will check +// for updates when connected to the cloud. +func WithCheckForUpdateIntervalWhenConnected(interval time.Duration) Option { + return func(cfg *reconcilerConfig) { + cfg.checkForUpdateIntervalWhenConnected = interval + } +} + +// WithSyncBackoffMaxInterval configures the maximum interval between sync attempts. +func WithSyncBackoffMaxInterval(interval time.Duration) Option { + return func(cfg *reconcilerConfig) { + cfg.syncBackoffMaxInterval = interval + } +} + +func newConfig(opts ...Option) *reconcilerConfig { + cfg := &reconcilerConfig{} + for _, opt := range []Option{ + WithTemporaryDirectory(os.TempDir()), + WithDownloadHTTPClient(http.DefaultClient), + WithDatabrokerRPSLimit(1_000), + WithCheckForUpdateIntervalWhenDisconnected(time.Minute * 5), + WithCheckForUpdateIntervalWhenConnected(time.Hour), + WithSyncBackoffMaxInterval(time.Minute), + } { + opt(cfg) + } + + for _, opt := range opts { + opt(cfg) + } + + return cfg +} diff --git a/internal/zero/reconciler/databroker.go b/internal/zero/reconciler/databroker.go new file mode 100644 index 00000000000..f38f0c2bde6 --- /dev/null +++ b/internal/zero/reconciler/databroker.go @@ -0,0 +1,81 @@ +package reconciler + +import ( + "context" + "errors" + "fmt" + "io" + + "google.golang.org/protobuf/proto" + + "github.com/pomerium/pomerium/pkg/grpc/databroker" +) + +// DatabrokerRecord is a wrapper around a databroker record. +type DatabrokerRecord struct { + V *databroker.Record +} + +var _ Record[DatabrokerRecord] = DatabrokerRecord{} + +// GetID returns the databroker record's ID. +func (r DatabrokerRecord) GetID() string { + return r.V.GetId() +} + +// GetType returns the databroker record's type. +func (r DatabrokerRecord) GetType() string { + return r.V.GetType() +} + +// Equal returns true if the databroker records are equal. +func (r DatabrokerRecord) Equal(other DatabrokerRecord) bool { + return r.V.Type == other.V.Type && + r.V.Id == other.V.Id && + proto.Equal(r.V.Data, other.V.Data) +} + +// GetDatabrokerRecords gets all databroker records of the given types. +func GetDatabrokerRecords( + ctx context.Context, + client databroker.DataBrokerServiceClient, + types []string, +) (RecordSetBundle[DatabrokerRecord], error) { + rsb := make(RecordSetBundle[DatabrokerRecord]) + + for _, typ := range types { + recs, err := getDatabrokerRecords(ctx, client, typ) + if err != nil { + return nil, fmt.Errorf("get databroker records for type %s: %w", typ, err) + } + rsb[typ] = recs + } + + return rsb, nil +} + +func getDatabrokerRecords( + ctx context.Context, + client databroker.DataBrokerServiceClient, + typ string, +) (RecordSet[DatabrokerRecord], error) { + stream, err := client.SyncLatest(ctx, &databroker.SyncLatestRequest{Type: typ}) + if err != nil { + return nil, fmt.Errorf("sync latest databroker: %w", err) + } + + recordSet := make(RecordSet[DatabrokerRecord]) + for { + res, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } else if err != nil { + return nil, fmt.Errorf("receive databroker record: %w", err) + } + + if record := res.GetRecord(); record != nil { + recordSet[record.GetId()] = DatabrokerRecord{record} + } + } + return recordSet, nil +} diff --git a/internal/zero/reconciler/databroker_changeset.go b/internal/zero/reconciler/databroker_changeset.go new file mode 100644 index 00000000000..1da2109163e --- /dev/null +++ b/internal/zero/reconciler/databroker_changeset.go @@ -0,0 +1,53 @@ +package reconciler + +import ( + "context" + "fmt" + + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/pomerium/pomerium/pkg/grpc/databroker" +) + +// DatabrokerChangeSet is a set of databroker changes. +type DatabrokerChangeSet struct { + now *timestamppb.Timestamp + updates []*databroker.Record +} + +// NewDatabrokerChangeSet creates a new databroker change set. +func NewDatabrokerChangeSet() *DatabrokerChangeSet { + return &DatabrokerChangeSet{ + now: timestamppb.Now(), + } +} + +// Remove adds a record to the change set. +func (cs *DatabrokerChangeSet) Remove(typ string, id string) { + cs.updates = append(cs.updates, &databroker.Record{ + Type: typ, + Id: id, + DeletedAt: cs.now, + }) +} + +// Upsert adds a record to the change set. +func (cs *DatabrokerChangeSet) Upsert(record *databroker.Record) { + cs.updates = append(cs.updates, &databroker.Record{ + Type: record.Type, + Id: record.Id, + Data: record.Data, + }) +} + +// ApplyChanges applies the changes to the databroker. +func ApplyChanges(ctx context.Context, client databroker.DataBrokerServiceClient, changes *DatabrokerChangeSet) error { + updates := databroker.OptimumPutRequestsFromRecords(changes.updates) + for _, req := range updates { + _, err := client.Put(ctx, req) + if err != nil { + return fmt.Errorf("put databroker record: %w", err) + } + } + return nil +} diff --git a/internal/zero/reconciler/download_cache.go b/internal/zero/reconciler/download_cache.go new file mode 100644 index 00000000000..2a709965bab --- /dev/null +++ b/internal/zero/reconciler/download_cache.go @@ -0,0 +1,168 @@ +package reconciler + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/go-multierror" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/pkg/grpc/databroker" + "github.com/pomerium/pomerium/pkg/protoutil" + zero_sdk "github.com/pomerium/zero-sdk" +) + +// BundleCacheEntry is a cache entry for a bundle +// that is kept in the databroker to avoid downloading +// the same bundle multiple times. +// +// by using the ETag and LastModified headers, we do not need to +// keep caches of the bundles themselves, which can be large. +// +// also it works in case of multiple instances, as it uses +// the databroker database as a shared cache. +type BundleCacheEntry struct { + zero_sdk.DownloadConditional + RecordTypes []string +} + +const ( + bundleCacheEntryRecordType = "pomerium.io/BundleCacheEntry" +) + +var ( + // ErrBundleCacheEntryNotFound is returned when a bundle cache entry is not found + ErrBundleCacheEntryNotFound = errors.New("bundle cache entry not found") +) + +// GetBundleCacheEntry gets a bundle cache entry from the databroker +func (c *service) GetBundleCacheEntry(ctx context.Context, id string) (*BundleCacheEntry, error) { + record, err := c.config.databrokerClient.Get(ctx, &databroker.GetRequest{ + Type: bundleCacheEntryRecordType, + Id: id, + }) + if err != nil && status.Code(err) == codes.NotFound { + return nil, ErrBundleCacheEntryNotFound + } else if err != nil { + return nil, fmt.Errorf("get bundle cache entry: %w", err) + } + + var dst BundleCacheEntry + data := record.GetRecord().GetData() + err = dst.FromAny(data) + if err != nil { + log.Ctx(ctx).Error().Err(err). + Str("bundle-id", id). + Str("data", protojson.Format(data)). + Msg("could not unmarshal bundle cache entry") + // we would allow it to be overwritten by the update process + return nil, ErrBundleCacheEntryNotFound + } + + return &dst, nil +} + +// SetBundleCacheEntry sets a bundle cache entry in the databroker +func (c *service) SetBundleCacheEntry(ctx context.Context, id string, src BundleCacheEntry) error { + val, err := src.ToAny() + if err != nil { + return fmt.Errorf("marshal bundle cache entry: %w", err) + } + _, err = c.config.databrokerClient.Put(ctx, &databroker.PutRequest{ + Records: []*databroker.Record{ + { + Type: bundleCacheEntryRecordType, + Id: id, + Data: val, + }, + }, + }) + if err != nil { + return fmt.Errorf("set bundle cache entry: %w", err) + } + return nil +} + +// ToAny marshals a BundleCacheEntry into an anypb.Any +func (r *BundleCacheEntry) ToAny() (*anypb.Any, error) { + err := r.Validate() + if err != nil { + return nil, fmt.Errorf("validate: %w", err) + } + + types := make([]*structpb.Value, 0, len(r.RecordTypes)) + for _, t := range r.RecordTypes { + types = append(types, structpb.NewStringValue(t)) + } + + return protoutil.NewAny(&structpb.Struct{ + Fields: map[string]*structpb.Value{ + "etag": structpb.NewStringValue(r.ETag), + "last_modified": structpb.NewStringValue(r.LastModified), + "record_types": structpb.NewListValue(&structpb.ListValue{Values: types}), + }, + }), nil +} + +// FromAny unmarshals an anypb.Any into a BundleCacheEntry +func (r *BundleCacheEntry) FromAny(any *anypb.Any) error { + var s structpb.Struct + err := any.UnmarshalTo(&s) + if err != nil { + return fmt.Errorf("unmarshal struct: %w", err) + } + + r.ETag = s.GetFields()["etag"].GetStringValue() + r.LastModified = s.GetFields()["last_modified"].GetStringValue() + + for _, v := range s.GetFields()["record_types"].GetListValue().GetValues() { + r.RecordTypes = append(r.RecordTypes, v.GetStringValue()) + } + + err = r.Validate() + if err != nil { + return fmt.Errorf("validate: %w", err) + } + return nil +} + +// Validate validates a BundleCacheEntry +func (r *BundleCacheEntry) Validate() error { + var errs *multierror.Error + if len(r.RecordTypes) == 0 { + errs = multierror.Append(errs, errors.New("record_types is required")) + } + if err := r.DownloadConditional.Validate(); err != nil { + errs = multierror.Append(errs, err) + } + return errs.ErrorOrNil() +} + +// GetDownloadConditional returns conditional download information +func (r *BundleCacheEntry) GetDownloadConditional() *zero_sdk.DownloadConditional { + if r == nil { + return nil + } + cond := r.DownloadConditional + return &cond +} + +// GetRecordTypes returns the record types +func (r *BundleCacheEntry) GetRecordTypes() []string { + if r == nil { + return nil + } + return r.RecordTypes +} + +// Equals returns true if the two cache entries are equal +func (r *BundleCacheEntry) Equals(other *BundleCacheEntry) bool { + return r != nil && other != nil && + r.ETag == other.ETag && r.LastModified == other.LastModified +} diff --git a/internal/zero/reconciler/download_cache_test.go b/internal/zero/reconciler/download_cache_test.go new file mode 100644 index 00000000000..f0d608a98eb --- /dev/null +++ b/internal/zero/reconciler/download_cache_test.go @@ -0,0 +1,29 @@ +package reconciler_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/zero/reconciler" + zero_sdk "github.com/pomerium/zero-sdk" +) + +func TestCacheEntryProto(t *testing.T) { + t.Parallel() + + original := reconciler.BundleCacheEntry{ + DownloadConditional: zero_sdk.DownloadConditional{ + ETag: "etag value", + LastModified: "2009-02-13 18:31:30 -0500 EST", + }, + RecordTypes: []string{"one", "two"}, + } + originalProto, err := original.ToAny() + require.NoError(t, err) + var unmarshaled reconciler.BundleCacheEntry + err = unmarshaled.FromAny(originalProto) + require.NoError(t, err) + assert.True(t, original.Equals(&unmarshaled)) +} diff --git a/internal/zero/reconciler/reconciler.go b/internal/zero/reconciler/reconciler.go new file mode 100644 index 00000000000..aa1b237d42b --- /dev/null +++ b/internal/zero/reconciler/reconciler.go @@ -0,0 +1,34 @@ +package reconciler + +import ( + "context" + "fmt" + + "github.com/pomerium/pomerium/pkg/grpc/databroker" +) + +// Reconcile reconciles the target and current record sets with the databroker. +func Reconcile( + ctx context.Context, + client databroker.DataBrokerServiceClient, + target, current RecordSetBundle[DatabrokerRecord], +) error { + updates := NewDatabrokerChangeSet() + + for _, rec := range current.GetRemoved(target).Flatten() { + updates.Remove(rec.GetType(), rec.GetID()) + } + for _, rec := range current.GetModified(target).Flatten() { + updates.Upsert(rec.V) + } + for _, rec := range current.GetAdded(target).Flatten() { + updates.Upsert(rec.V) + } + + err := ApplyChanges(ctx, client, updates) + if err != nil { + return fmt.Errorf("apply databroker changes: %w", err) + } + + return nil +} diff --git a/internal/zero/reconciler/reconciler_test.go b/internal/zero/reconciler/reconciler_test.go new file mode 100644 index 00000000000..9935c58e584 --- /dev/null +++ b/internal/zero/reconciler/reconciler_test.go @@ -0,0 +1,196 @@ +package reconciler_test + +import ( + "context" + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/test/bufconn" + "google.golang.org/protobuf/types/known/wrapperspb" + + databroker_int "github.com/pomerium/pomerium/internal/databroker" + "github.com/pomerium/pomerium/internal/zero/reconciler" + "github.com/pomerium/pomerium/pkg/grpc/databroker" + "github.com/pomerium/pomerium/pkg/protoutil" +) + +func newDatabroker(t *testing.T) (context.Context, databroker.DataBrokerServiceClient) { + t.Helper() + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + gs := grpc.NewServer() + srv := databroker_int.New() + + databroker.RegisterDataBrokerServiceServer(gs, srv) + + lis := bufconn.Listen(1) + t.Cleanup(func() { + lis.Close() + gs.Stop() + }) + + go func() { _ = gs.Serve(lis) }() + + conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(func(context.Context, string) (conn net.Conn, e error) { + return lis.Dial() + }), grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + t.Cleanup(func() { _ = conn.Close() }) + + return ctx, databroker.NewDataBrokerServiceClient(conn) +} + +func newRecordBundle(records []testRecord) reconciler.RecordSetBundle[reconciler.DatabrokerRecord] { + bundle := make(reconciler.RecordSetBundle[reconciler.DatabrokerRecord]) + for _, r := range records { + bundle.Add(newRecord(r)) + } + return bundle +} + +func newRecord(r testRecord) reconciler.DatabrokerRecord { + return reconciler.DatabrokerRecord{ + V: &databroker.Record{ + Type: r.Type, + Id: r.ID, + Data: protoutil.NewAnyString(r.Val), + }} +} + +func assertBundle(t *testing.T, want []testRecord, got reconciler.RecordSetBundle[reconciler.DatabrokerRecord]) { + t.Helper() + + for _, wantRecord := range want { + gotRecord, ok := got.Get(wantRecord.Type, wantRecord.ID) + if assert.True(t, ok, "record %s/%s not found", wantRecord.Type, wantRecord.ID) { + assertRecord(t, wantRecord, gotRecord) + } + } + assert.Len(t, got.Flatten(), len(want)) +} + +func assertRecord(t *testing.T, want testRecord, got reconciler.DatabrokerRecord) { + t.Helper() + + var val wrapperspb.StringValue + err := got.V.Data.UnmarshalTo(&val) + require.NoError(t, err) + + assert.Equal(t, want.Type, got.V.Type) + assert.Equal(t, want.ID, got.V.Id) + assert.Equal(t, want.Val, val.Value) +} + +func TestHelpers(t *testing.T) { + want := []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id2", "value2"}, + } + + bundle := newRecordBundle(want) + assertBundle(t, want, bundle) +} + +func wantRemoved(want, current []string) []string { + wantM := make(map[string]struct{}, len(want)) + for _, w := range want { + wantM[w] = struct{}{} + } + var toRemove []string + for _, c := range current { + if _, ok := wantM[c]; !ok { + toRemove = append(toRemove, c) + } + } + return toRemove +} + +func reconcile( + ctx context.Context, + t *testing.T, + client databroker.DataBrokerServiceClient, + want []testRecord, + current reconciler.RecordSetBundle[reconciler.DatabrokerRecord], +) reconciler.RecordSetBundle[reconciler.DatabrokerRecord] { + t.Helper() + + wantBundle := newRecordBundle(want) + err := reconciler.Reconcile(ctx, client, wantBundle, current) + require.NoError(t, err) + + got, err := reconciler.GetDatabrokerRecords(ctx, client, wantBundle.RecordTypes()) + require.NoError(t, err) + assertBundle(t, want, got) + + res, err := reconciler.GetDatabrokerRecords(ctx, client, wantRemoved(wantBundle.RecordTypes(), current.RecordTypes())) + require.NoError(t, err) + assert.Empty(t, res.Flatten()) + + return got +} + +func TestReconcile(t *testing.T) { + t.Parallel() + + ctx, client := newDatabroker(t) + + err := reconciler.Reconcile(ctx, client, nil, nil) + require.NoError(t, err) + + var current reconciler.RecordSetBundle[reconciler.DatabrokerRecord] + for _, tc := range []struct { + name string + want []testRecord + }{ + {"empty", nil}, + {"initial", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id2", "value2"}, + }}, + {"add one", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id2", "value2"}, + {"type1", "id3", "value3"}, + }}, + {"update one", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id2", "value2-updated"}, + {"type1", "id3", "value3"}, + }}, + {"delete one", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id3", "value3"}, + }}, + {"delete all", nil}, + {"multiple types", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id2", "value2"}, + {"type2", "id1", "value1"}, + {"type2", "id2", "value2"}, + }}, + {"multiple types update", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id2", "value2-updated"}, + {"type2", "id1", "value1"}, + {"type2", "id2", "value2-updated"}, + }}, + {"multiple types delete", []testRecord{ + {"type1", "id1", "value1"}, + {"type2", "id1", "value1"}, + }}, + {"multiple types delete one type, add one value", []testRecord{ + {"type1", "id1", "value1"}, + {"type1", "id4", "value4"}, + }}, + } { + t.Run(tc.name, func(t *testing.T) { + current = reconcile(ctx, t, client, tc.want, current) + }) + } +} diff --git a/internal/zero/reconciler/records.go b/internal/zero/reconciler/records.go new file mode 100644 index 00000000000..b6094a615ad --- /dev/null +++ b/internal/zero/reconciler/records.go @@ -0,0 +1,132 @@ +package reconciler + +// RecordSetBundle is an index of databroker records by type +type RecordSetBundle[T Record[T]] map[string]RecordSet[T] + +// RecordSet is an index of databroker records by their id. +type RecordSet[T Record[T]] map[string]T + +// Record is a record +type Record[T any] interface { + GetID() string + GetType() string + Equal(other T) bool +} + +// RecordTypes returns the types of records in the bundle. +func (rsb RecordSetBundle[T]) RecordTypes() []string { + types := make([]string, 0, len(rsb)) + for typ := range rsb { + types = append(types, typ) + } + return types +} + +// Add adds a record to the bundle. +func (rsb RecordSetBundle[T]) Add(record T) { + rs, ok := rsb[record.GetType()] + if !ok { + rs = make(RecordSet[T]) + rsb[record.GetType()] = rs + } + rs[record.GetID()] = record +} + +// GetAdded returns the records that are in other but not in rsb. +func (rsb RecordSetBundle[T]) GetAdded(other RecordSetBundle[T]) RecordSetBundle[T] { + added := make(RecordSetBundle[T]) + for otherType, otherRS := range other { + rs, ok := rsb[otherType] + if !ok { + added[otherType] = otherRS + continue + } + rss := rs.GetAdded(other[otherType]) + if len(rss) > 0 { + added[otherType] = rss + } + } + return added +} + +// GetRemoved returns the records that are in rs but not in other. +func (rsb RecordSetBundle[T]) GetRemoved(other RecordSetBundle[T]) RecordSetBundle[T] { + return other.GetAdded(rsb) +} + +// GetModified returns the records that are in both rs and other but have different data. +func (rsb RecordSetBundle[T]) GetModified(other RecordSetBundle[T]) RecordSetBundle[T] { + modified := make(RecordSetBundle[T]) + for otherType, otherRS := range other { + rs, ok := rsb[otherType] + if !ok { + continue + } + m := rs.GetModified(otherRS) + if len(m) > 0 { + modified[otherType] = m + } + } + return modified +} + +// GetAdded returns the records that are in other but not in rs. +func (rs RecordSet[T]) GetAdded(other RecordSet[T]) RecordSet[T] { + added := make(RecordSet[T]) + for id, record := range other { + if _, ok := rs[id]; !ok { + added[id] = record + } + } + return added +} + +// GetRemoved returns the records that are in rs but not in other. +func (rs RecordSet[T]) GetRemoved(other RecordSet[T]) RecordSet[T] { + return other.GetAdded(rs) +} + +// GetModified returns the records that are in both rs and other but have different data. +// by comparing the protobuf bytes of the payload. +func (rs RecordSet[T]) GetModified(other RecordSet[T]) RecordSet[T] { + modified := make(RecordSet[T]) + for id, record := range other { + otherRecord, ok := rs[id] + if !ok { + continue + } + + if !record.Equal(otherRecord) { + modified[id] = record + } + } + return modified +} + +// Flatten returns all records in the set. +func (rs RecordSet[T]) Flatten() []T { + records := make([]T, 0, len(rs)) + for _, record := range rs { + records = append(records, record) + } + return records +} + +// Flatten returns all records in the bundle. +func (rsb RecordSetBundle[T]) Flatten() []T { + records := make([]T, 0) + for _, rs := range rsb { + records = append(records, rs.Flatten()...) + } + return records +} + +// Get returns a record by type and id. +func (rsb RecordSetBundle[T]) Get(typeName, id string) (record T, ok bool) { + rs, ok := rsb[typeName] + if !ok { + return + } + record, ok = rs[id] + return +} diff --git a/internal/zero/reconciler/records_test.go b/internal/zero/reconciler/records_test.go new file mode 100644 index 00000000000..5d1864538ac --- /dev/null +++ b/internal/zero/reconciler/records_test.go @@ -0,0 +1,77 @@ +package reconciler_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/pomerium/pomerium/internal/zero/reconciler" +) + +type testRecord struct { + Type string + ID string + Val string +} + +func (r testRecord) GetID() string { + return r.ID +} + +func (r testRecord) GetType() string { + return r.Type +} + +func (r testRecord) Equal(other testRecord) bool { + return r.ID == other.ID && r.Type == other.Type && r.Val == other.Val +} + +func TestRecords(t *testing.T) { + initial := make(reconciler.RecordSetBundle[testRecord]) + initial.Add(testRecord{ID: "1", Type: "a", Val: "a-1"}) + initial.Add(testRecord{ID: "2", Type: "a", Val: "a-2"}) + initial.Add(testRecord{ID: "1", Type: "b", Val: "b-1"}) + + // test record types + assert.ElementsMatch(t, []string{"a", "b"}, initial.RecordTypes()) + + // test added, deleted and modified + updated := make(reconciler.RecordSetBundle[testRecord]) + updated.Add(testRecord{ID: "1", Type: "a", Val: "a-1-1"}) + updated.Add(testRecord{ID: "3", Type: "a", Val: "a-3"}) + updated.Add(testRecord{ID: "1", Type: "b", Val: "b-1"}) + updated.Add(testRecord{ID: "2", Type: "b", Val: "b-2"}) + updated.Add(testRecord{ID: "1", Type: "c", Val: "c-1"}) + + assert.ElementsMatch(t, []string{"a", "b", "c"}, updated.RecordTypes()) + + added := initial.GetAdded(updated) + assert.Equal(t, + reconciler.RecordSetBundle[testRecord]{ + "a": reconciler.RecordSet[testRecord]{ + "3": {ID: "3", Type: "a", Val: "a-3"}, + }, + "b": reconciler.RecordSet[testRecord]{ + "2": {ID: "2", Type: "b", Val: "b-2"}, + }, + "c": reconciler.RecordSet[testRecord]{ + "1": {ID: "1", Type: "c", Val: "c-1"}, + }, + }, added) + + removed := initial.GetRemoved(updated) + assert.Equal(t, + reconciler.RecordSetBundle[testRecord]{ + "a": reconciler.RecordSet[testRecord]{ + "2": {ID: "2", Type: "a", Val: "a-2"}, + }, + }, removed) + + modified := initial.GetModified(updated) + assert.Equal(t, + reconciler.RecordSetBundle[testRecord]{ + "a": reconciler.RecordSet[testRecord]{ + "1": {ID: "1", Type: "a", Val: "a-1-1"}, + }, + }, modified) +} diff --git a/internal/zero/reconciler/service.go b/internal/zero/reconciler/service.go new file mode 100644 index 00000000000..1fb951d5954 --- /dev/null +++ b/internal/zero/reconciler/service.go @@ -0,0 +1,89 @@ +package reconciler + +/* + * This is a main control loop for the reconciler service. + * + */ + +import ( + "context" + "time" + + "golang.org/x/sync/errgroup" + "golang.org/x/time/rate" + + "github.com/pomerium/pomerium/internal/atomicutil" + connect_mux "github.com/pomerium/zero-sdk/connect-mux" +) + +type service struct { + config *reconcilerConfig + + databrokerRateLimit *rate.Limiter + + bundles BundleQueue + + fullSyncRequest chan struct{} + bundleSyncRequest chan struct{} + periodicUpdateInterval atomicutil.Value[time.Duration] +} + +// Run creates a new bundle updater client +// that runs until the context is canceled or a fatal error occurs. +func Run(ctx context.Context, opts ...Option) error { + config := newConfig(opts...) + + c := &service{ + config: config, + databrokerRateLimit: rate.NewLimiter(rate.Limit(config.databrokerRPS), 1), + fullSyncRequest: make(chan struct{}, 1), + } + c.periodicUpdateInterval.Store(config.checkForUpdateIntervalWhenDisconnected) + + eg, ctx := errgroup.WithContext(ctx) + eg.Go(func() error { return c.watchUpdates(ctx) }) + eg.Go(func() error { return c.SyncLoop(ctx) }) + + return eg.Wait() +} + +// run is a main control loop. +// it is very simple and sequential download and reconcile. +// it may be later optimized by splitting between download and reconciliation process, +// as we would get more resource bundles beyond the config. +func (c *service) watchUpdates(ctx context.Context) error { + return c.config.api.Watch(ctx, + connect_mux.WithOnConnected(func(ctx context.Context) { + c.triggerFullUpdate(true) + }), + connect_mux.WithOnDisconnected(func(_ context.Context) { + c.triggerFullUpdate(false) + }), + connect_mux.WithOnBundleUpdated(func(_ context.Context, key string) { + c.triggerBundleUpdate(key) + }), + ) +} + +func (c *service) triggerBundleUpdate(id string) { + c.periodicUpdateInterval.Store(c.config.checkForUpdateIntervalWhenConnected) + c.bundles.MarkForSync(id) + + select { + case c.fullSyncRequest <- struct{}{}: + default: + } +} + +func (c *service) triggerFullUpdate(connected bool) { + timeout := c.config.checkForUpdateIntervalWhenDisconnected + if connected { + timeout = c.config.checkForUpdateIntervalWhenConnected + } + c.periodicUpdateInterval.Store(timeout) + + select { + case c.fullSyncRequest <- struct{}{}: + default: + } +} diff --git a/internal/zero/reconciler/sync.go b/internal/zero/reconciler/sync.go new file mode 100644 index 00000000000..0630d0b9117 --- /dev/null +++ b/internal/zero/reconciler/sync.go @@ -0,0 +1,231 @@ +package reconciler + +/* + * Sync syncs the bundles between their cloud source and the databroker. + * + * FullSync performs a full sync of the bundles by calling the API, + * and walking the list of bundles, and calling SyncBundle on each. + * It also removes any records in the databroker that are not in the list of bundles. + * + * WatchAndSync watches the API for changes, and calls SyncBundle on each change. + * + */ + +import ( + "context" + "errors" + "fmt" + "io" + "time" + + "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/retry" +) + +// Sync synchronizes the bundles between their cloud source and the databroker. +func (c *service) SyncLoop(ctx context.Context) error { + ticker := time.NewTicker(c.periodicUpdateInterval.Load()) + defer ticker.Stop() + + for { + dur := c.periodicUpdateInterval.Load() + ticker.Reset(dur) + + select { + case <-ctx.Done(): + return ctx.Err() + case <-c.bundleSyncRequest: + log.Ctx(ctx).Info().Msg("bundle sync triggered") + err := c.syncBundles(ctx) + if err != nil { + return fmt.Errorf("reconciler: sync bundles: %w", err) + } + case <-c.fullSyncRequest: + log.Ctx(ctx).Info().Msg("full sync triggered") + err := c.syncAll(ctx) + if err != nil { + return fmt.Errorf("reconciler: sync all: %w", err) + } + case <-ticker.C: + log.Ctx(ctx).Info().Msg("periodic sync triggered") + err := c.syncAll(ctx) + if err != nil { + return fmt.Errorf("reconciler: sync all: %w", err) + } + } + } +} + +func (c *service) syncAll(ctx context.Context) error { + err := c.syncBundleList(ctx) + if err != nil { + return fmt.Errorf("sync bundle list: %w", err) + } + + err = c.syncBundles(ctx) + if err != nil { + return fmt.Errorf("sync bundles: %w", err) + } + + return nil +} + +// trySyncAllBundles tries to sync all bundles in the queue. +func (c *service) syncBundleList(ctx context.Context) error { + // refresh bundle list, + // ignoring other signals while we're retrying + return retry.Retry(ctx, + "refresh bundle list", c.refreshBundleList, + retry.WithWatch("refresh bundle list", c.fullSyncRequest, nil), + retry.WithWatch("bundle update", c.bundleSyncRequest, nil), + ) +} + +// syncBundles retries until there are no more bundles to sync. +// updates bundle list if the full bundle update request arrives. +func (c *service) syncBundles(ctx context.Context) error { + return retry.Retry(ctx, + "sync bundles", c.trySyncBundles, + retry.WithWatch("refresh bundle list", c.fullSyncRequest, c.refreshBundleList), + retry.WithWatch("bundle update", c.bundleSyncRequest, nil), + ) +} + +// trySyncAllBundles tries to sync all bundles in the queue +// it returns nil if all bundles were synced successfully +func (c *service) trySyncBundles(ctx context.Context) error { + for { + id, ok := c.bundles.GetNextBundleToSync() + if !ok { // no more bundles to sync + return nil + } + + err := c.syncBundle(ctx, id) + if err != nil { + c.bundles.MarkForSyncLater(id) + return fmt.Errorf("sync bundle %s: %w", id, err) + } + } +} + +// syncBundle syncs the bundle to the databroker. +// Databroker holds last synced bundle state in form of a (etag, last-modified) tuple. +// This is only persisted in the databroker after all records are successfully synced. +// That allows us to ignore any changes based on the same bundle state, without need to re-check all records between bundle and databroker. +func (c *service) syncBundle(ctx context.Context, key string) error { + cached, err := c.GetBundleCacheEntry(ctx, key) + if err != nil && !errors.Is(err, ErrBundleCacheEntryNotFound) { + return fmt.Errorf("get bundle cache entry: %w", err) + } + + // download is much faster compared to databroker sync, + // so we don't use pipe but rather download to a temp file and then sync it to databroker + fd, err := c.GetTmpFile(key) + if err != nil { + return fmt.Errorf("get tmp file: %w", err) + } + defer func() { + if err := fd.Close(); err != nil { + log.Ctx(ctx).Error().Err(err).Msg("close tmp file") + } + }() + + conditional := cached.GetDownloadConditional() + log.Ctx(ctx).Debug().Str("id", key).Any("conditional", conditional).Msg("downloading bundle") + + result, err := c.config.api.DownloadClusterResourceBundle(ctx, fd, key, conditional) + if err != nil { + return fmt.Errorf("download bundle: %w", err) + } + + if result.NotModified { + log.Ctx(ctx).Debug().Str("bundle", key).Msg("bundle not changed") + return nil + } + + log.Ctx(ctx).Debug().Str("bundle", key). + Interface("cached-entry", cached). + Interface("current-entry", result.DownloadConditional). + Msg("bundle updated") + + _, err = fd.Seek(0, io.SeekStart) + if err != nil { + return fmt.Errorf("seek to start: %w", err) + } + + bundleRecordTypes, err := c.syncBundleToDatabroker(ctx, fd, cached.GetRecordTypes()) + if err != nil { + return fmt.Errorf("apply bundle to databroker: %w", err) + } + current := BundleCacheEntry{ + DownloadConditional: *result.DownloadConditional, + RecordTypes: bundleRecordTypes, + } + + log.Ctx(ctx).Info(). + Str("bundle", key). + Strs("record_types", bundleRecordTypes). + Str("etag", current.ETag). + Str("last_modified", current.LastModified). + Msg("bundle synced") + + err = c.SetBundleCacheEntry(ctx, key, current) + if err != nil { + return fmt.Errorf("set bundle cache entry: %w", err) + } + + return nil +} + +func strUnion(a, b []string) []string { + m := make(map[string]struct{}, len(a)+len(b)) + for _, s := range a { + m[s] = struct{}{} + } + for _, s := range b { + m[s] = struct{}{} + } + + out := make([]string, 0, len(m)) + for s := range m { + out = append(out, s) + } + return out +} + +func (c *service) syncBundleToDatabroker(ctx context.Context, src io.Reader, currentRecordTypes []string) ([]string, error) { + bundleRecords, err := ReadBundleRecords(src) + if err != nil { + return nil, fmt.Errorf("read bundle records: %w", err) + } + + databrokerRecords, err := GetDatabrokerRecords(ctx, + c.config.databrokerClient, + strUnion(bundleRecords.RecordTypes(), currentRecordTypes), + ) + if err != nil { + return nil, fmt.Errorf("get databroker records: %w", err) + } + + err = Reconcile(ctx, c.config.databrokerClient, bundleRecords, databrokerRecords) + if err != nil { + return nil, fmt.Errorf("reconcile databroker records: %w", err) + } + + return bundleRecords.RecordTypes(), nil +} + +func (c *service) refreshBundleList(ctx context.Context) error { + resp, err := c.config.api.GetClusterResourceBundles(ctx) + if err != nil { + return fmt.Errorf("get bundles: %w", err) + } + + ids := make([]string, 0, len(resp.Bundles)) + for _, v := range resp.Bundles { + ids = append(ids, v.Id) + } + + c.bundles.Set(ids) + return nil +} diff --git a/internal/zero/reconciler/tmpfile.go b/internal/zero/reconciler/tmpfile.go new file mode 100644 index 00000000000..527186bd6c7 --- /dev/null +++ b/internal/zero/reconciler/tmpfile.go @@ -0,0 +1,40 @@ +package reconciler + +import ( + "fmt" + "io" + "os" + + "github.com/hashicorp/go-multierror" +) + +// ReadWriteSeekCloser is a file that can be read, written, seeked, and closed. +type ReadWriteSeekCloser interface { + io.ReadWriteSeeker + io.Closer +} + +// GetTmpFile returns a temporary file for the reconciler to use. +// TODO: encrypt contents to ensure encryption at rest +func (c *service) GetTmpFile(key string) (ReadWriteSeekCloser, error) { + fd, err := os.CreateTemp(c.config.tmpDir, fmt.Sprintf("pomerium-bundle-%s", key)) + if err != nil { + return nil, fmt.Errorf("create temp file: %w", err) + } + return &tmpFile{File: fd}, nil +} + +type tmpFile struct { + *os.File +} + +func (f *tmpFile) Close() error { + var errs *multierror.Error + if err := f.File.Close(); err != nil { + errs = multierror.Append(errs, err) + } + if err := os.Remove(f.File.Name()); err != nil { + errs = multierror.Append(errs, err) + } + return errs.ErrorOrNil() +} From a78fc6fbfa61f1bb3a17cac7b1c6799053b2a809 Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Thu, 17 Aug 2023 14:22:52 -0400 Subject: [PATCH 4/9] zero: managed mode controller (#4459) --- cmd/pomerium/main.go | 8 ++- go.mod | 2 +- internal/zero/cmd/command.go | 83 +++++++++++++++++++++ internal/zero/cmd/env.go | 13 ++++ internal/zero/cmd/env_dev.go | 21 ++++++ internal/zero/cmd/env_release.go | 11 +++ internal/zero/controller/config.go | 86 ++++++++++++++++++++++ internal/zero/controller/controller.go | 99 ++++++++++++++++++++++++++ internal/zero/controller/databroker.go | 46 ++++++++++++ internal/zero/controller/mux_log.go | 29 ++++++++ internal/zero/controller/reconciler.go | 21 ++++++ 11 files changed, 417 insertions(+), 2 deletions(-) create mode 100644 internal/zero/cmd/command.go create mode 100644 internal/zero/cmd/env.go create mode 100644 internal/zero/cmd/env_dev.go create mode 100644 internal/zero/cmd/env_release.go create mode 100644 internal/zero/controller/config.go create mode 100644 internal/zero/controller/controller.go create mode 100644 internal/zero/controller/databroker.go create mode 100644 internal/zero/controller/mux_log.go create mode 100644 internal/zero/controller/reconciler.go diff --git a/cmd/pomerium/main.go b/cmd/pomerium/main.go index ad3703d6f6f..f4f29043887 100644 --- a/cmd/pomerium/main.go +++ b/cmd/pomerium/main.go @@ -12,6 +12,7 @@ import ( "github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/version" + zero_cmd "github.com/pomerium/pomerium/internal/zero/cmd" "github.com/pomerium/pomerium/pkg/cmd/pomerium" "github.com/pomerium/pomerium/pkg/envoy/files" ) @@ -30,7 +31,12 @@ func main() { } ctx := context.Background() - if err := run(ctx); !errors.Is(err, context.Canceled) { + runFn := run + if zero_cmd.IsManagedMode() { + runFn = zero_cmd.Run + } + + if err := runFn(ctx); err != nil && !errors.Is(err, context.Canceled) { log.Fatal().Err(err).Msg("cmd/pomerium") } log.Info(ctx).Msg("cmd/pomerium: exiting") diff --git a/go.mod b/go.mod index d366f10577f..e2e369cf52d 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/jackc/pgx/v5 v5.4.3 github.com/klauspost/compress v1.17.0 github.com/martinlindhe/base36 v1.1.1 + github.com/mattn/go-isatty v0.0.19 github.com/mholt/acmez v1.2.0 github.com/minio/minio-go/v7 v7.0.63 github.com/mitchellh/hashstructure/v2 v2.0.2 @@ -184,7 +185,6 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.55 // indirect github.com/minio/md5-simd v1.1.2 // indirect diff --git a/internal/zero/cmd/command.go b/internal/zero/cmd/command.go new file mode 100644 index 00000000000..24321b4f0cd --- /dev/null +++ b/internal/zero/cmd/command.go @@ -0,0 +1,83 @@ +// Package cmd implements the pomerium zero command. +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/mattn/go-isatty" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + + "github.com/pomerium/pomerium/internal/zero/controller" +) + +// Run runs the pomerium zero command. +func Run(ctx context.Context) error { + err := setupLogger() + if err != nil { + return fmt.Errorf("error setting up logger: %w", err) + } + + token := getToken() + if token == "" { + return errors.New("no token provided") + } + + return controller.Run( + withInterrupt(ctx), + controller.WithAPIToken(token), + controller.WithClusterAPIEndpoint(getClusterAPIEndpoint()), + controller.WithConnectAPIEndpoint(getConnectAPIEndpoint()), + ) +} + +// IsManagedMode returns true if Pomerium should start in managed mode using this command. +func IsManagedMode() bool { + return getToken() != "" +} + +func withInterrupt(ctx context.Context) context.Context { + ctx, cancel := context.WithCancel(ctx) + go func(ctx context.Context) { + ch := make(chan os.Signal, 2) + defer signal.Stop(ch) + + signal.Notify(ch, os.Interrupt) + signal.Notify(ch, syscall.SIGTERM) + + select { + case sig := <-ch: + log.Ctx(ctx).Info().Str("signal", sig.String()).Msg("quitting...") + case <-ctx.Done(): + } + cancel() + }(ctx) + return ctx +} + +func setupLogger() error { + if isatty.IsTerminal(os.Stdin.Fd()) { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + } else { + log.Logger = zerolog.New(os.Stderr) + } + + if rawLvl, ok := os.LookupEnv("LOG_LEVEL"); ok { + lvl, err := zerolog.ParseLevel(rawLvl) + if err != nil { + return err + } + log.Logger = log.Logger.Level(lvl) + } else { + log.Logger = log.Logger.Level(zerolog.InfoLevel) + } + + // set the default context logger + zerolog.DefaultContextLogger = &log.Logger + return nil +} diff --git a/internal/zero/cmd/env.go b/internal/zero/cmd/env.go new file mode 100644 index 00000000000..5ee6a3c3782 --- /dev/null +++ b/internal/zero/cmd/env.go @@ -0,0 +1,13 @@ +package cmd + +import "os" + +const ( + // PomeriumZeroTokenEnv is the environment variable name for the API token. + //nolint: gosec + PomeriumZeroTokenEnv = "POMERIUM_ZERO_TOKEN" +) + +func getToken() string { + return os.Getenv(PomeriumZeroTokenEnv) +} diff --git a/internal/zero/cmd/env_dev.go b/internal/zero/cmd/env_dev.go new file mode 100644 index 00000000000..99123ff4eed --- /dev/null +++ b/internal/zero/cmd/env_dev.go @@ -0,0 +1,21 @@ +//go:build !release + +package cmd + +import "os" + +func getConnectAPIEndpoint() string { + connectServerEndpoint := os.Getenv("CONNECT_SERVER_ENDPOINT") + if connectServerEndpoint == "" { + connectServerEndpoint = "http://localhost:8721" + } + return connectServerEndpoint +} + +func getClusterAPIEndpoint() string { + clusterAPIEndpoint := os.Getenv("CLUSTER_API_ENDPOINT") + if clusterAPIEndpoint == "" { + clusterAPIEndpoint = "http://localhost:8720/cluster/v1" + } + return clusterAPIEndpoint +} diff --git a/internal/zero/cmd/env_release.go b/internal/zero/cmd/env_release.go new file mode 100644 index 00000000000..1438d40c928 --- /dev/null +++ b/internal/zero/cmd/env_release.go @@ -0,0 +1,11 @@ +//go:build release + +package cmd + +func getConnectAPIEndpoint() string { + return "https://connect.pomerium.com" +} + +func getClusterAPIEndpoint() string { + return "https://console.pomerium.com/cluster/v1" +} diff --git a/internal/zero/controller/config.go b/internal/zero/controller/config.go new file mode 100644 index 00000000000..0e729acb504 --- /dev/null +++ b/internal/zero/controller/config.go @@ -0,0 +1,86 @@ +package controller + +import "time" + +// Option configures a controller. +type Option func(*controllerConfig) + +type controllerConfig struct { + apiToken string + clusterAPIEndpoint string + connectAPIEndpoint string + + tmpDir string + bootstrapConfigFileName string + + reconcilerLeaseDuration time.Duration + databrokerRequestTimeout time.Duration +} + +// WithTmpDir sets the temporary directory to use. +func WithTmpDir(dir string) Option { + return func(c *controllerConfig) { + c.tmpDir = dir + } +} + +// WithClusterAPIEndpoint sets the endpoint to use for the cluster API +func WithClusterAPIEndpoint(endpoint string) Option { + return func(c *controllerConfig) { + c.clusterAPIEndpoint = endpoint + } +} + +// WithConnectAPIEndpoint sets the endpoint to use for the connect API +func WithConnectAPIEndpoint(endpoint string) Option { + return func(c *controllerConfig) { + c.connectAPIEndpoint = endpoint + } +} + +// WithAPIToken sets the API token to use for authentication. +func WithAPIToken(token string) Option { + return func(c *controllerConfig) { + c.apiToken = token + } +} + +// WithBootstrapConfigFileName sets the name of the file to store the bootstrap config in. +func WithBootstrapConfigFileName(name string) Option { + return func(c *controllerConfig) { + c.bootstrapConfigFileName = name + } +} + +// WithDatabrokerLeaseDuration sets the lease duration for the +func WithDatabrokerLeaseDuration(duration time.Duration) Option { + return func(c *controllerConfig) { + c.reconcilerLeaseDuration = duration + } +} + +// WithDatabrokerRequestTimeout sets the timeout for databroker requests. +func WithDatabrokerRequestTimeout(timeout time.Duration) Option { + return func(c *controllerConfig) { + c.databrokerRequestTimeout = timeout + } +} + +func newControllerConfig(opts ...Option) *controllerConfig { + c := new(controllerConfig) + + for _, opt := range []Option{ + WithClusterAPIEndpoint("https://console.pomerium.com/cluster/v1"), + WithConnectAPIEndpoint("https://connect.pomerium.com"), + WithBootstrapConfigFileName("/var/cache/pomerium-bootstrap.dat"), + WithDatabrokerLeaseDuration(time.Second * 30), + WithDatabrokerRequestTimeout(time.Second * 30), + } { + opt(c) + } + + for _, opt := range opts { + opt(c) + } + return c +} diff --git a/internal/zero/controller/controller.go b/internal/zero/controller/controller.go new file mode 100644 index 00000000000..d1377168e5b --- /dev/null +++ b/internal/zero/controller/controller.go @@ -0,0 +1,99 @@ +// Package controller implements Pomerium managed mode +package controller + +import ( + "context" + "errors" + "fmt" + + "golang.org/x/sync/errgroup" + + "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/internal/zero/bootstrap" + "github.com/pomerium/pomerium/pkg/cmd/pomerium" + "github.com/pomerium/pomerium/pkg/grpc/databroker" + sdk "github.com/pomerium/zero-sdk" +) + +// Run runs Pomerium is managed mode using the provided token. +func Run(ctx context.Context, opts ...Option) error { + c := controller{cfg: newControllerConfig(opts...)} + eg, ctx := errgroup.WithContext(ctx) + + err := c.initAPI(ctx) + if err != nil { + return fmt.Errorf("init api: %w", err) + } + + src, err := bootstrap.New([]byte(c.cfg.apiToken)) + if err != nil { + return fmt.Errorf("error creating bootstrap config: %w", err) + } + c.bootstrapConfig = src + + err = c.InitDatabrokerClient(ctx, src.GetConfig()) + if err != nil { + return fmt.Errorf("init databroker client: %w", err) + } + + eg.Go(func() error { return run(ctx, "connect", c.runConnect, nil) }) + eg.Go(func() error { return run(ctx, "zero-bootstrap", c.runBootstrap, nil) }) + eg.Go(func() error { return run(ctx, "pomerium-core", c.runPomeriumCore, src.WaitReady) }) + eg.Go(func() error { return run(ctx, "zero-reconciler", c.RunReconciler, src.WaitReady) }) + eg.Go(func() error { return run(ctx, "connect-log", c.RunConnectLog, nil) }) + return eg.Wait() +} + +type controller struct { + cfg *controllerConfig + + api *sdk.API + + bootstrapConfig *bootstrap.Source + + databrokerClient databroker.DataBrokerServiceClient +} + +func (c *controller) initAPI(ctx context.Context) error { + api, err := sdk.NewAPI(ctx, + sdk.WithClusterAPIEndpoint(c.cfg.clusterAPIEndpoint), + sdk.WithAPIToken(c.cfg.apiToken), + sdk.WithConnectAPIEndpoint(c.cfg.connectAPIEndpoint), + ) + if err != nil { + return fmt.Errorf("error initializing cloud api: %w", err) + } + + c.api = api + + return nil +} + +func run(ctx context.Context, name string, runFn func(context.Context) error, waitFn func(context.Context) error) error { + if waitFn != nil { + log.Ctx(ctx).Info().Str("name", name).Msg("waiting for initial configuration") + err := waitFn(ctx) + if err != nil { + return fmt.Errorf("%s: error waiting for initial configuration: %w", name, err) + } + } + + log.Ctx(ctx).Info().Str("name", name).Msg("starting") + err := runFn(ctx) + if err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("%s: %w", name, err) + } + return nil +} + +func (c *controller) runBootstrap(ctx context.Context) error { + return c.bootstrapConfig.Run(ctx, c.api, c.cfg.bootstrapConfigFileName) +} + +func (c *controller) runPomeriumCore(ctx context.Context) error { + return pomerium.Run(ctx, c.bootstrapConfig) +} + +func (c *controller) runConnect(ctx context.Context) error { + return c.api.Connect(ctx) +} diff --git a/internal/zero/controller/databroker.go b/internal/zero/controller/databroker.go new file mode 100644 index 00000000000..0008f6713ae --- /dev/null +++ b/internal/zero/controller/databroker.go @@ -0,0 +1,46 @@ +package controller + +import ( + "context" + "encoding/base64" + "fmt" + "net" + "net/url" + + "google.golang.org/grpc" + + "github.com/pomerium/pomerium/config" + "github.com/pomerium/pomerium/pkg/grpc/databroker" + "github.com/pomerium/pomerium/pkg/grpcutil" +) + +func (c *controller) InitDatabrokerClient(ctx context.Context, cfg *config.Config) error { + conn, err := c.newDataBrokerConnection(ctx, cfg) + if err != nil { + return fmt.Errorf("databroker connection: %w", err) + } + c.databrokerClient = databroker.NewDataBrokerServiceClient(conn) + return nil +} + +// GetDataBrokerServiceClient implements the databroker.Leaser interface. +func (c *controller) GetDataBrokerServiceClient() databroker.DataBrokerServiceClient { + return c.databrokerClient +} + +func (c *controller) newDataBrokerConnection(ctx context.Context, cfg *config.Config) (*grpc.ClientConn, error) { + sharedSecret, err := base64.StdEncoding.DecodeString(cfg.Options.SharedKey) + if err != nil { + return nil, fmt.Errorf("decode shared_secret: %w", err) + } + + return grpcutil.NewGRPCClientConn(ctx, &grpcutil.Options{ + Address: &url.URL{ + Scheme: "http", + Host: net.JoinHostPort("localhost", cfg.GRPCPort), + }, + ServiceName: "databroker", + SignedJWTKey: sharedSecret, + RequestTimeout: c.cfg.databrokerRequestTimeout, + }) +} diff --git a/internal/zero/controller/mux_log.go b/internal/zero/controller/mux_log.go new file mode 100644 index 00000000000..b0cad8d5858 --- /dev/null +++ b/internal/zero/controller/mux_log.go @@ -0,0 +1,29 @@ +package controller + +import ( + "context" + + "github.com/rs/zerolog" + + "github.com/pomerium/pomerium/internal/log" + connect_mux "github.com/pomerium/zero-sdk/connect-mux" +) + +func (c *controller) RunConnectLog(ctx context.Context) error { + logger := log.Ctx(ctx).With().Str("service", "connect-mux").Logger().Level(zerolog.InfoLevel) + + return c.api.Watch(ctx, + connect_mux.WithOnConnected(func(ctx context.Context) { + logger.Info().Msg("connected") + }), + connect_mux.WithOnDisconnected(func(ctx context.Context) { + logger.Info().Msg("disconnected") + }), + connect_mux.WithOnBootstrapConfigUpdated(func(ctx context.Context) { + logger.Info().Msg("bootstrap config updated") + }), + connect_mux.WithOnBundleUpdated(func(ctx context.Context, key string) { + logger.Info().Str("key", key).Msg("bundle updated") + }), + ) +} diff --git a/internal/zero/controller/reconciler.go b/internal/zero/controller/reconciler.go new file mode 100644 index 00000000000..9c6993e87eb --- /dev/null +++ b/internal/zero/controller/reconciler.go @@ -0,0 +1,21 @@ +package controller + +import ( + "context" + + "github.com/pomerium/pomerium/internal/zero/reconciler" + "github.com/pomerium/pomerium/pkg/grpc/databroker" +) + +func (c *controller) RunReconciler(ctx context.Context) error { + leaser := databroker.NewLeaser("zero-reconciler", c.cfg.reconcilerLeaseDuration, c) + return leaser.Run(ctx) +} + +// RunLeased implements the databroker.Leaser interface. +func (c *controller) RunLeased(ctx context.Context) error { + return reconciler.Run(ctx, + reconciler.WithAPI(c.api), + reconciler.WithDataBrokerClient(c.GetDataBrokerServiceClient()), + ) +} From b61fa100026c9ce1e40de0053b184b54c3f193b3 Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Thu, 17 Aug 2023 18:15:58 -0400 Subject: [PATCH 5/9] zero: rm extra call on start (#4474) --- internal/zero/bootstrap/bootstrap.go | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/internal/zero/bootstrap/bootstrap.go b/internal/zero/bootstrap/bootstrap.go index 6cb176c7058..ddea80c3d79 100644 --- a/internal/zero/bootstrap/bootstrap.go +++ b/internal/zero/bootstrap/bootstrap.go @@ -42,7 +42,7 @@ func (svc *Source) Run( svc.api = api svc.fileCachePath = fileCachePath - svc.tryLoadInitial(ctx) + svc.tryLoadFromFile(ctx) eg, ctx := errgroup.WithContext(ctx) eg.Go(func() error { return svc.watchUpdates(ctx) }) @@ -70,6 +70,14 @@ func (svc *Source) updateLoop(ctx context.Context) error { defer ticker.Stop() for { + err := retry.Retry(ctx, + "update bootstrap", svc.updateAndSave, + retry.WithWatch("bootstrap config updated", svc.checkForUpdate, nil), + ) + if err != nil { + return fmt.Errorf("update bootstrap config: %w", err) + } + ticker.Reset(svc.updateInterval.Load()) select { @@ -78,14 +86,6 @@ func (svc *Source) updateLoop(ctx context.Context) error { case <-svc.checkForUpdate: case <-ticker.C: } - - err := retry.Retry(ctx, - "update bootstrap", svc.updateAndSave, - retry.WithWatch("bootstrap config updated", svc.checkForUpdate, nil), - ) - if err != nil { - return fmt.Errorf("update bootstrap config: %w", err) - } } } @@ -116,15 +116,6 @@ func (svc *Source) updateAndSave(ctx context.Context) error { return nil } -func (svc *Source) tryLoadInitial(ctx context.Context) { - err := svc.updateAndSave(ctx) - if err != nil { - log.Ctx(ctx).Error().Err(err).Msg("failed to load bootstrap config") - svc.tryLoadFromFile(ctx) - return - } -} - func (svc *Source) tryLoadFromFile(ctx context.Context) { cfg, err := LoadBootstrapConfigFromFile(svc.fileCachePath, svc.fileCipher) if err != nil { From 8468f103fd1a8a882053e5e40cf0381e3df7d0e7 Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Wed, 11 Oct 2023 10:25:34 -0400 Subject: [PATCH 6/9] zero: report resource bundle reconciliation status (#4618) * zero: report resource bundle reconciliation status * use latest zero-sdk --- go.mod | 32 +++------ go.sum | 83 ++++++----------------- internal/zero/reconciler/report_status.go | 44 ++++++++++++ internal/zero/reconciler/sync.go | 7 +- 4 files changed, 77 insertions(+), 89 deletions(-) create mode 100644 internal/zero/reconciler/report_status.go diff --git a/go.mod b/go.mod index e2e369cf52d..3a0568de957 100644 --- a/go.mod +++ b/go.mod @@ -50,7 +50,7 @@ require ( github.com/pomerium/csrf v1.7.0 github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 github.com/pomerium/webauthn v0.0.0-20221118023040-00a9c430578b - github.com/pomerium/zero-sdk v0.0.0-20230816163741-2c7886877a34 + github.com/pomerium/zero-sdk v0.0.0-20231006190436-fa42b41aa1a9 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 github.com/prometheus/common v0.44.0 @@ -109,19 +109,17 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect github.com/aws/smithy-go v1.14.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bufbuild/buf v1.25.0 // indirect + github.com/bufbuild/buf v1.26.1 // indirect github.com/bufbuild/connect-go v1.9.0 // indirect github.com/bufbuild/connect-opentelemetry-go v0.4.0 // indirect - github.com/bufbuild/protocompile v0.5.1 // indirect - github.com/bytedance/sonic v1.9.1 // indirect + github.com/bufbuild/protocompile v0.6.0 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect github.com/containerd/continuity v0.4.2 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/deepmap/oapi-codegen v1.13.2 // indirect + github.com/deepmap/oapi-codegen v1.15.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/cli v24.0.4+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect @@ -132,10 +130,7 @@ require ( github.com/felixge/fgprof v0.9.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/getkin/kin-openapi v0.118.0 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -144,12 +139,8 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.21.1 // 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.14.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -176,9 +167,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/labstack/echo/v4 v4.11.1 // indirect - github.com/labstack/gommon v0.4.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -195,10 +183,11 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/oapi-codegen/runtime v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc4 // indirect github.com/opencontainers/runc v1.1.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/philhofer/fwd v1.0.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect @@ -220,15 +209,11 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect - github.com/tetratelabs/wazero v1.3.0 // indirect + github.com/tetratelabs/wazero v1.3.1 // indirect github.com/tinylib/msgp v1.1.2 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect - github.com/ugorji/go/codec v1.2.11 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -244,12 +229,11 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.3.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.11.0 // indirect + golang.org/x/tools v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect diff --git a/go.sum b/go.sum index 3cdb66c1cfc..699b4f53b51 100644 --- a/go.sum +++ b/go.sum @@ -133,19 +133,16 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/bufbuild/buf v1.25.0 h1:HFxKrR8wFcZwrBInN50K/oJX/WOtPVq24rHb/ArjfBA= -github.com/bufbuild/buf v1.25.0/go.mod h1:GCKZ5bAP6Ht4MF7KcfaGVgBEXGumwAz2hXjjLVxx8ZU= +github.com/bufbuild/buf v1.26.1 h1:+GdU4z2paCmDclnjLv7MqnVi3AGviImlIKhG0MHH9FA= +github.com/bufbuild/buf v1.26.1/go.mod h1:UMPncXMWgrmIM+0QpwTEwjNr2SA0z2YIVZZsmNflvB4= github.com/bufbuild/connect-go v1.9.0 h1:JIgAeNuFpo+SUPfU19Yt5TcWlznsN5Bv10/gI/6Pjoc= github.com/bufbuild/connect-go v1.9.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8= github.com/bufbuild/connect-opentelemetry-go v0.4.0 h1:6JAn10SNqlQ/URhvRNGrIlczKw1wEXknBUUtmWqOiak= github.com/bufbuild/connect-opentelemetry-go v0.4.0/go.mod h1:nwPXYoDOoc2DGyKE/6pT1Q9MPSi2Et2e6BieMD0l6WU= -github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg= -github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY8+wjsYLR/lc40= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0= github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -160,9 +157,6 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL 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/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -202,8 +196,8 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG 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/deepmap/oapi-codegen v1.13.2 h1:I/REY+90rmcheXdR5rCg9j+7wAI134UwBk44AYt5QAY= -github.com/deepmap/oapi-codegen v1.13.2/go.mod h1:eXAuxgJu9XC+dZECAw9cF4qtmL0qwGy0ZAvV6A2j1oA= +github.com/deepmap/oapi-codegen v1.15.0 h1:SQqViaeb4k2vMul8gx12oDOIadEtoRqTdLkxjzqtQ90= +github.com/deepmap/oapi-codegen v1.15.0/go.mod h1:a6KoHV7lMRwsPoEg2C6NDHiXYV3EQfiFocOlJ8dgJQE= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= @@ -260,15 +254,9 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -301,14 +289,6 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -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= -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-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= @@ -318,8 +298,6 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= @@ -371,8 +349,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k= github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -515,7 +491,6 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -533,12 +508,6 @@ 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/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= -github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= -github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= -github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= @@ -556,10 +525,8 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg= github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -605,6 +572,8 @@ github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjH github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= +github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -629,8 +598,8 @@ github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hP github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= @@ -659,8 +628,8 @@ github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 h1:3YQY1sb5 github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524/go.mod h1:7fGbUYJnU8RcxZJvUvhukOIBv1G7LWDAHMfDxAf5+Y0= github.com/pomerium/webauthn v0.0.0-20221118023040-00a9c430578b h1:oll/aOfJudnqFAwCvoXK9+WN2zVjTzHVPLXCggHQmHk= github.com/pomerium/webauthn v0.0.0-20221118023040-00a9c430578b/go.mod h1:KswTenBBh4y1pmhU2dpm8VgJQCgSErCg7OOFTeebrNc= -github.com/pomerium/zero-sdk v0.0.0-20230816163741-2c7886877a34 h1:UMt+h1nKW9DDCYzejkGaYFjKzOHFgIP9zorRMADbKhM= -github.com/pomerium/zero-sdk v0.0.0-20230816163741-2c7886877a34/go.mod h1:cAyfEGM8blUzchYhOWrufuj/6lOF277meB4c/TjMS28= +github.com/pomerium/zero-sdk v0.0.0-20231006190436-fa42b41aa1a9 h1:vnD0LJMOOZ388Tx4OKOOPd7pnTiUA+Li/kdhYleA7Ys= +github.com/pomerium/zero-sdk v0.0.0-20231006190436-fa42b41aa1a9/go.mod h1:abFyZev+/xx+KBg2OD3oJxhcMH96pfChFw6lOnCsvfc= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -773,8 +742,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= @@ -783,8 +750,8 @@ github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNG github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/tetratelabs/wazero v1.3.0 h1:nqw7zCldxE06B8zSZAY0ACrR9OH5QCcPwYmYlwtcwtE= -github.com/tetratelabs/wazero v1.3.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= +github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM= +github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -794,11 +761,10 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f h1:C43EMGXFtvYf/zunHR6ivZV7Z6ytg73t0GXwYyicXMQ= github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f/go.mod h1:N+sR0vLSCTtI6o06PMWsjMB4TVqqDttKNq4iC9wvxVY= -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/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= @@ -807,11 +773,6 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g= github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= @@ -889,9 +850,6 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -929,6 +887,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1069,12 +1029,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1162,8 +1119,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= 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= @@ -1310,7 +1267,6 @@ 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.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/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= @@ -1326,7 +1282,6 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 namespacelabs.dev/go-filenotify v0.0.0-20220511192020-53ea11be7eaa h1:jj2kjs0Hvufj40wuhMAzoZUOwrwMDFg1gHZ49RiIv9w= namespacelabs.dev/go-filenotify v0.0.0-20220511192020-53ea11be7eaa/go.mod h1:e8NJRaInXRRm1+KPA6EkGEzdLJAgEvVSIKiLzpP97nI= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/internal/zero/reconciler/report_status.go b/internal/zero/reconciler/report_status.go new file mode 100644 index 00000000000..9331253fa2e --- /dev/null +++ b/internal/zero/reconciler/report_status.go @@ -0,0 +1,44 @@ +package reconciler + +import ( + "context" + + "github.com/pomerium/pomerium/internal/log" + cluster_api "github.com/pomerium/zero-sdk/cluster" +) + +const ( + // BundleStatusFailureDatabrokerError indicates a failure due to a databroker error + BundleStatusFailureDatabrokerError = cluster_api.DatabrokerError + // BundleStatusFailureDownloadError indicates a failure due to a download error + BundleStatusFailureDownloadError = cluster_api.DownloadError + // BundleStatusFailureInvalidBundle indicates a failure due to an invalid bundle + BundleStatusFailureInvalidBundle = cluster_api.InvalidBundle + // BundleStatusFailureIO indicates a failure due to an IO error + BundleStatusFailureIO = cluster_api.IoError + // BundleStatusFailureUnknownError indicates a failure due to an unknown error + BundleStatusFailureUnknownError = cluster_api.UnknownError +) + +func (c *service) ReportBundleAppliedSuccess( + ctx context.Context, + bundleID string, + metadata map[string]string, +) { + err := c.config.api.ReportBundleAppliedSuccess(ctx, bundleID, metadata) + if err != nil { + log.Ctx(ctx).Err(err).Msg("reconciler: error reporting bundle status") + } +} + +func (c *service) ReportBundleAppliedFailure( + ctx context.Context, + bundleID string, + source cluster_api.BundleStatusFailureSource, + err error, +) { + err = c.config.api.ReportBundleAppliedFailure(ctx, bundleID, source, err) + if err != nil { + log.Ctx(ctx).Err(err).Msg("reconciler: error reporting bundle status") + } +} diff --git a/internal/zero/reconciler/sync.go b/internal/zero/reconciler/sync.go index 0630d0b9117..55cae2ebded 100644 --- a/internal/zero/reconciler/sync.go +++ b/internal/zero/reconciler/sync.go @@ -135,6 +135,7 @@ func (c *service) syncBundle(ctx context.Context, key string) error { result, err := c.config.api.DownloadClusterResourceBundle(ctx, fd, key, conditional) if err != nil { + c.ReportBundleAppliedFailure(ctx, key, BundleStatusFailureDownloadError, err) return fmt.Errorf("download bundle: %w", err) } @@ -155,6 +156,7 @@ func (c *service) syncBundle(ctx context.Context, key string) error { bundleRecordTypes, err := c.syncBundleToDatabroker(ctx, fd, cached.GetRecordTypes()) if err != nil { + c.ReportBundleAppliedFailure(ctx, key, BundleStatusFailureDatabrokerError, err) return fmt.Errorf("apply bundle to databroker: %w", err) } current := BundleCacheEntry{ @@ -171,9 +173,12 @@ func (c *service) syncBundle(ctx context.Context, key string) error { err = c.SetBundleCacheEntry(ctx, key, current) if err != nil { - return fmt.Errorf("set bundle cache entry: %w", err) + err = fmt.Errorf("set bundle cache entry: %w", err) + c.ReportBundleAppliedFailure(ctx, key, BundleStatusFailureDatabrokerError, err) + return err } + c.ReportBundleAppliedSuccess(ctx, key, result.Metadata) return nil } From 0c762ed326a6c2a07235c990d8ded1d2fbf02cd0 Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Thu, 12 Oct 2023 11:19:57 -0400 Subject: [PATCH 7/9] zero: restart config reconciliation when databroker storage is changed (#4623) --- internal/zero/controller/reconciler.go | 7 -- internal/zero/reconciler/download_cache.go | 7 +- internal/zero/reconciler/restart.go | 72 ++++++++++++++ internal/zero/reconciler/restart_test.go | 110 +++++++++++++++++++++ internal/zero/reconciler/service.go | 45 +++++++++ 5 files changed, 231 insertions(+), 10 deletions(-) create mode 100644 internal/zero/reconciler/restart.go create mode 100644 internal/zero/reconciler/restart_test.go diff --git a/internal/zero/controller/reconciler.go b/internal/zero/controller/reconciler.go index 9c6993e87eb..f8ecd4aacc5 100644 --- a/internal/zero/controller/reconciler.go +++ b/internal/zero/controller/reconciler.go @@ -4,16 +4,9 @@ import ( "context" "github.com/pomerium/pomerium/internal/zero/reconciler" - "github.com/pomerium/pomerium/pkg/grpc/databroker" ) func (c *controller) RunReconciler(ctx context.Context) error { - leaser := databroker.NewLeaser("zero-reconciler", c.cfg.reconcilerLeaseDuration, c) - return leaser.Run(ctx) -} - -// RunLeased implements the databroker.Leaser interface. -func (c *controller) RunLeased(ctx context.Context) error { return reconciler.Run(ctx, reconciler.WithAPI(c.api), reconciler.WithDataBrokerClient(c.GetDataBrokerServiceClient()), diff --git a/internal/zero/reconciler/download_cache.go b/internal/zero/reconciler/download_cache.go index 2a709965bab..ec6b295c005 100644 --- a/internal/zero/reconciler/download_cache.go +++ b/internal/zero/reconciler/download_cache.go @@ -33,7 +33,8 @@ type BundleCacheEntry struct { } const ( - bundleCacheEntryRecordType = "pomerium.io/BundleCacheEntry" + // BundleCacheEntryRecordType is the databroker record type for BundleCacheEntry + BundleCacheEntryRecordType = "pomerium.io/BundleCacheEntry" ) var ( @@ -44,7 +45,7 @@ var ( // GetBundleCacheEntry gets a bundle cache entry from the databroker func (c *service) GetBundleCacheEntry(ctx context.Context, id string) (*BundleCacheEntry, error) { record, err := c.config.databrokerClient.Get(ctx, &databroker.GetRequest{ - Type: bundleCacheEntryRecordType, + Type: BundleCacheEntryRecordType, Id: id, }) if err != nil && status.Code(err) == codes.NotFound { @@ -77,7 +78,7 @@ func (c *service) SetBundleCacheEntry(ctx context.Context, id string, src Bundle _, err = c.config.databrokerClient.Put(ctx, &databroker.PutRequest{ Records: []*databroker.Record{ { - Type: bundleCacheEntryRecordType, + Type: BundleCacheEntryRecordType, Id: id, Data: val, }, diff --git a/internal/zero/reconciler/restart.go b/internal/zero/reconciler/restart.go new file mode 100644 index 00000000000..079b0930248 --- /dev/null +++ b/internal/zero/reconciler/restart.go @@ -0,0 +1,72 @@ +package reconciler + +import ( + "context" + "fmt" + "sync" +) + +// RunWithRestart executes execFn. +// The execution would be restarted, by means of canceling the context provided to execFn, each time restartFn quits. +// the error returned by restartFn is purely informational and does not affect the execution; may be nil. +// the loop is stopped when the context provided to RunWithRestart is canceled or a genuine error is returned by execFn, not caused by the context. +func RunWithRestart( + ctx context.Context, + execFn func(context.Context) error, + restartFn func(context.Context) error, +) error { + contexts := make(chan context.Context) + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var wg sync.WaitGroup + wg.Add(2) + + var err error + go func() { + err = restartWithContext(contexts, execFn) + cancel() + wg.Done() + }() + go func() { + restartContexts(ctx, contexts, restartFn) + wg.Done() + }() + + wg.Wait() + return err +} + +func restartContexts( + base context.Context, + contexts chan<- context.Context, + restartFn func(context.Context) error, +) { + defer close(contexts) + for base.Err() == nil { + ctx, cancel := context.WithCancelCause(base) + select { + case contexts <- ctx: + err := restartFn(ctx) + cancel(fmt.Errorf("requesting restart: %w", err)) + case <-base.Done(): + cancel(fmt.Errorf("parent context canceled: %w", base.Err())) + return + } + } +} + +func restartWithContext( + contexts <-chan context.Context, + execFn func(context.Context) error, +) error { + var err error + for ctx := range contexts { + err = execFn(ctx) + if ctx.Err() == nil { + return err + } + } + return err +} diff --git a/internal/zero/reconciler/restart_test.go b/internal/zero/reconciler/restart_test.go new file mode 100644 index 00000000000..2fbe0fdff84 --- /dev/null +++ b/internal/zero/reconciler/restart_test.go @@ -0,0 +1,110 @@ +package reconciler_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/pomerium/pomerium/internal/zero/reconciler" +) + +func TestRestart(t *testing.T) { + t.Parallel() + + for i := 0; i < 20; i++ { + t.Run(fmt.Sprintf("quit on error %d", i), func(t *testing.T) { + t.Parallel() + + errExpected := errors.New("execFn error") + count := 0 + err := reconciler.RunWithRestart(context.Background(), + func(context.Context) error { + count++ + if count == 1 { + return errExpected + } + return errors.New("execFn should not be called more than once") + }, + func(ctx context.Context) error { + <-ctx.Done() + return ctx.Err() + }, + ) + assert.ErrorIs(t, err, errExpected) + }) + + t.Run(fmt.Sprintf("quit on no error %d", i), func(t *testing.T) { + t.Parallel() + + count := 0 + err := reconciler.RunWithRestart(context.Background(), + func(context.Context) error { + count++ + if count == 1 { + return nil + } + return errors.New("execFn should not be called more than once") + }, + func(ctx context.Context) error { + <-ctx.Done() + return ctx.Err() + }, + ) + assert.NoError(t, err) + }) + + t.Run(fmt.Sprintf("parent context canceled %d", i), func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + ready := make(chan struct{}) + err := reconciler.RunWithRestart(ctx, + func(context.Context) error { + <-ready + cancel() + return ctx.Err() + }, + func(context.Context) error { + close(ready) + <-ctx.Done() + return ctx.Err() + }, + ) + assert.ErrorIs(t, err, context.Canceled) + }) + + t.Run(fmt.Sprintf("triggers restart %d", i), func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + errExpected := errors.New("execFn error") + count := 0 + ready := make(chan struct{}) + err := reconciler.RunWithRestart(ctx, + func(ctx context.Context) error { + count++ + if count == 1 { // wait for us to be restarted + close(ready) + <-ctx.Done() + return ctx.Err() + } else if count == 2 { // just quit + return errExpected + } + return errors.New("execFn should not be called more than twice") + }, + func(ctx context.Context) error { + <-ready + return errors.New("restart required") + }, + ) + assert.ErrorIs(t, err, errExpected) + }) + } +} diff --git a/internal/zero/reconciler/service.go b/internal/zero/reconciler/service.go index 1fb951d5954..7ab537ae59b 100644 --- a/internal/zero/reconciler/service.go +++ b/internal/zero/reconciler/service.go @@ -7,12 +7,14 @@ package reconciler import ( "context" + "fmt" "time" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" "github.com/pomerium/pomerium/internal/atomicutil" + "github.com/pomerium/pomerium/pkg/grpc/databroker" connect_mux "github.com/pomerium/zero-sdk/connect-mux" ) @@ -40,6 +42,11 @@ func Run(ctx context.Context, opts ...Option) error { } c.periodicUpdateInterval.Store(config.checkForUpdateIntervalWhenDisconnected) + return c.runMainLoop(ctx) +} + +// RunLeased implements the databroker.LeaseHandler interface +func (c *service) RunLeased(ctx context.Context) error { eg, ctx := errgroup.WithContext(ctx) eg.Go(func() error { return c.watchUpdates(ctx) }) eg.Go(func() error { return c.SyncLoop(ctx) }) @@ -47,6 +54,44 @@ func Run(ctx context.Context, opts ...Option) error { return eg.Wait() } +// GetDataBrokerServiceClient implements the databroker.LeaseHandler interface. +func (c *service) GetDataBrokerServiceClient() databroker.DataBrokerServiceClient { + return c.config.databrokerClient +} + +func (c *service) runMainLoop(ctx context.Context) error { + leaser := databroker.NewLeaser("zero-reconciler", time.Second*30, c) + return RunWithRestart(ctx, func(ctx context.Context) error { + return leaser.Run(ctx) + }, c.databrokerChangeMonitor) +} + +// databrokerChangeMonitor runs infinite sync loop to see if there is any change in databroker +func (c *service) databrokerChangeMonitor(ctx context.Context) error { + _, recordVersion, serverVersion, err := databroker.InitialSync(ctx, c.GetDataBrokerServiceClient(), &databroker.SyncLatestRequest{ + Type: BundleCacheEntryRecordType, + }) + if err != nil { + return fmt.Errorf("error during initial sync: %w", err) + } + + stream, err := c.GetDataBrokerServiceClient().Sync(ctx, &databroker.SyncRequest{ + Type: BundleCacheEntryRecordType, + ServerVersion: serverVersion, + RecordVersion: recordVersion, + }) + if err != nil { + return fmt.Errorf("error calling sync: %w", err) + } + + for { + _, err := stream.Recv() + if err != nil { + return fmt.Errorf("error receiving record: %w", err) + } + } +} + // run is a main control loop. // it is very simple and sequential download and reconcile. // it may be later optimized by splitting between download and reconciliation process, From a4b2531d8eea7d632285191790b691b472575224 Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Fri, 20 Oct 2023 15:14:20 -0400 Subject: [PATCH 8/9] zero: derive signing key first thing (#4631) --- internal/zero/bootstrap/new.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/zero/bootstrap/new.go b/internal/zero/bootstrap/new.go index 2bafb577db5..0426a55f33f 100644 --- a/internal/zero/bootstrap/new.go +++ b/internal/zero/bootstrap/new.go @@ -43,14 +43,14 @@ func New(secret []byte) (*Source, error) { rnd := hkdf.New(sha256.New, secret, nil, nil) - cipher, err := initCipher(rnd) + err = initSecrets(cfg, rnd) if err != nil { - return nil, fmt.Errorf("init cypher: %w", err) + return nil, fmt.Errorf("init secrets: %w", err) } - err = initSecrets(cfg, rnd) + cipher, err := initCipher(rnd) if err != nil { - return nil, fmt.Errorf("init secrets: %w", err) + return nil, fmt.Errorf("init cypher: %w", err) } svc := &Source{ From 40956f18529c0e7716b2fe4672ecfc44f54932de Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 13 Nov 2023 16:50:35 -0700 Subject: [PATCH 9/9] core/zero: fix urls (#4743) --- internal/zero/cmd/env_release.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/zero/cmd/env_release.go b/internal/zero/cmd/env_release.go index 1438d40c928..277dfba99b2 100644 --- a/internal/zero/cmd/env_release.go +++ b/internal/zero/cmd/env_release.go @@ -3,9 +3,9 @@ package cmd func getConnectAPIEndpoint() string { - return "https://connect.pomerium.com" + return "https://connect.pomerium.app" } func getClusterAPIEndpoint() string { - return "https://console.pomerium.com/cluster/v1" + return "https://console.pomerium.app/cluster/v1" }