mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-10 23:57:34 +02:00
zero: merge pomerium/zero-sdk (#4848)
This commit is contained in:
parent
c4dd965f2d
commit
ea64902a73
54 changed files with 4800 additions and 170 deletions
49
go.mod
49
go.mod
|
@ -43,6 +43,7 @@ require (
|
|||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/natefinch/atomic v1.0.1
|
||||
github.com/oapi-codegen/runtime v1.1.0
|
||||
github.com/open-policy-agent/opa v0.59.0
|
||||
github.com/openzipkin/zipkin-go v0.4.2
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
|
@ -50,7 +51,6 @@ 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-20231127153820-dcd408d87b54
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/prometheus/common v0.45.0
|
||||
|
@ -67,7 +67,7 @@ require (
|
|||
go.uber.org/automaxprocs v1.5.3
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/crypto v0.16.0
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/oauth2 v0.15.0
|
||||
golang.org/x/sync v0.5.0
|
||||
|
@ -75,7 +75,7 @@ require (
|
|||
google.golang.org/api v0.153.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
google.golang.org/protobuf v1.31.1-0.20231027082548-f4a6c1f6e5c1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
namespacelabs.dev/go-filenotify v0.0.0-20220511192020-53ea11be7eaa
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
|
@ -110,45 +110,31 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect
|
||||
github.com/aws/smithy-go v1.16.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // 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.6.0 // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // 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.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.15.0 // 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/distribution/reference v0.5.0 // indirect
|
||||
github.com/docker/cli v24.0.7+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // 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.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.118.0 // 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.3.0 // 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-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // 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.2 // indirect
|
||||
|
@ -157,65 +143,49 @@ 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/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/matttproud/golang_protobuf_extensions/v2 v2.0.0 // 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/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/oapi-codegen/runtime v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
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
|
||||
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.8.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.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/uber/jaeger-client-go v2.25.0+incompatible // 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
|
||||
|
@ -227,19 +197,16 @@ require (
|
|||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/goleak v1.3.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.12.0 // indirect
|
||||
golang.org/x/tools v0.15.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // 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
|
||||
|
|
134
go.sum
134
go.sum
|
@ -55,7 +55,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
|||
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=
|
||||
|
@ -140,14 +139,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||
github.com/bits-and-blooms/bitset v1.11.0 h1:RMyy2mBBShArUAhfVRZJ2xyBO58KCBCtZFShw3umo6k=
|
||||
github.com/bits-and-blooms/bitset v1.11.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
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.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/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||
|
@ -179,8 +170,6 @@ 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=
|
||||
|
@ -194,18 +183,12 @@ 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/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/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.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=
|
||||
|
@ -214,14 +197,14 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
|||
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 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/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
|
||||
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+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=
|
||||
|
@ -243,8 +226,6 @@ 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.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
|
@ -261,8 +242,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
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/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/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
|
@ -291,25 +270,14 @@ 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-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-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
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/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=
|
||||
|
@ -379,8 +347,6 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/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=
|
||||
|
@ -406,9 +372,6 @@ 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=
|
||||
|
@ -426,7 +389,6 @@ github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qK
|
|||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
|
@ -456,14 +418,9 @@ 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=
|
||||
|
@ -472,15 +429,9 @@ github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw=
|
|||
github.com/jackc/pgx/v5 v5.5.0/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=
|
||||
|
@ -502,7 +453,6 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
|||
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=
|
||||
|
@ -526,11 +476,6 @@ 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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
@ -552,7 +497,6 @@ github.com/minio/minio-go/v7 v7.0.65 h1:sOlB8T3nQK+TApTpuN3k4WD5KasvZIE3vVFzyyCa
|
|||
github.com/minio/minio-go/v7 v7.0.65/go.mod h1:R4WVUR6ZTedlCcGwZRauLMIKjgyaWxhs4Mqi/OMPmEc=
|
||||
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=
|
||||
|
@ -569,8 +513,6 @@ 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=
|
||||
|
@ -578,12 +520,11 @@ 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.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
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/oapi-codegen/runtime v1.1.0 h1:rJpoNUawn5XTvekgfkvSZr0RqEnoYpFkyvrzfWeFKWM=
|
||||
github.com/oapi-codegen/runtime v1.1.0/go.mod h1:BeSfBkWWWnAnGdyS+S/GnlbmHKzf8/hwkvelJZDeKA8=
|
||||
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=
|
||||
|
@ -613,8 +554,6 @@ 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.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=
|
||||
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=
|
||||
|
@ -623,15 +562,11 @@ 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=
|
||||
|
@ -641,8 +576,6 @@ 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-20231127153820-dcd408d87b54 h1:pIjWOs15zJq9XhU1uHKhBjqc6X7cJHRYbx+OW4v3EiA=
|
||||
github.com/pomerium/zero-sdk v0.0.0-20231127153820-dcd408d87b54/go.mod h1:b3979nx45mwDaeIQQe5T11XjxMUQntc4H3v+szdsHUc=
|
||||
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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
|
@ -690,8 +623,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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
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/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=
|
||||
|
@ -700,8 +633,6 @@ 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=
|
||||
|
@ -716,7 +647,6 @@ 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=
|
||||
|
@ -729,8 +659,6 @@ 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.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
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=
|
||||
|
@ -765,8 +693,6 @@ 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.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=
|
||||
|
@ -779,19 +705,11 @@ github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f/go.mod h1:N+sR0vL
|
|||
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=
|
||||
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/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=
|
||||
|
@ -847,15 +765,11 @@ go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ3
|
|||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
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.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
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/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
|
@ -889,8 +803,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-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
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=
|
||||
|
@ -904,8 +818,6 @@ 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=
|
||||
|
@ -918,8 +830,8 @@ 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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=
|
||||
|
@ -1051,11 +963,9 @@ 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-20210906170528-6f6e22806c34/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-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1066,7 +976,6 @@ golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/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=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -1077,8 +986,6 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
|||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
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=
|
||||
|
@ -1148,8 +1055,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
|
|||
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
||||
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=
|
||||
|
@ -1248,8 +1155,6 @@ 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.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
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=
|
||||
|
@ -1265,16 +1170,14 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.1-0.20231027082548-f4a6c1f6e5c1 h1:fk72uXZyuZiTtW5tgd63jyVK6582lF61nRC/kGv6vCA=
|
||||
google.golang.org/protobuf v1.31.1-0.20231027082548-f4a6c1f6e5c1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.22.0 h1:gpWsqqkwUldNZXGJqT69NU9MdEDhLboK1C4nMgR0MWw=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.22.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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=
|
||||
|
@ -1296,7 +1199,6 @@ 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/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=
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
|
||||
"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"
|
||||
sdk "github.com/pomerium/pomerium/pkg/zero"
|
||||
connect_mux "github.com/pomerium/pomerium/pkg/zero/connect-mux"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
cluster_api "github.com/pomerium/zero-sdk/cluster"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
)
|
||||
|
||||
// LoadBootstrapConfigFromFile loads the bootstrap configuration from a file.
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/internal/zero/bootstrap"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
cluster_api "github.com/pomerium/zero-sdk/cluster"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
)
|
||||
|
||||
func TestFile(t *testing.T) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/deterministicecdsa"
|
||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||
"github.com/pomerium/pomerium/pkg/netutil"
|
||||
sdk "github.com/pomerium/zero-sdk"
|
||||
sdk "github.com/pomerium/pomerium/pkg/zero"
|
||||
)
|
||||
|
||||
// Source is a base config layer for Pomerium
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
cluster_api "github.com/pomerium/zero-sdk/cluster"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/zero/reconciler"
|
||||
"github.com/pomerium/pomerium/pkg/cmd/pomerium"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
sdk "github.com/pomerium/zero-sdk"
|
||||
sdk "github.com/pomerium/pomerium/pkg/zero"
|
||||
)
|
||||
|
||||
// Run runs Pomerium is managed mode using the provided token.
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
connect_mux "github.com/pomerium/zero-sdk/connect-mux"
|
||||
connect_mux "github.com/pomerium/pomerium/pkg/zero/connect-mux"
|
||||
)
|
||||
|
||||
func (c *controller) RunConnectLog(ctx context.Context) error {
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
sdk "github.com/pomerium/zero-sdk"
|
||||
sdk "github.com/pomerium/pomerium/pkg/zero"
|
||||
)
|
||||
|
||||
// reconcilerConfig contains the configuration for the resource bundles reconciler.
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"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"
|
||||
zero_sdk "github.com/pomerium/pomerium/pkg/zero"
|
||||
)
|
||||
|
||||
// BundleCacheEntry is a cache entry for a bundle
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/zero/reconciler"
|
||||
zero_sdk "github.com/pomerium/zero-sdk"
|
||||
zero_sdk "github.com/pomerium/pomerium/pkg/zero"
|
||||
)
|
||||
|
||||
func TestCacheEntryProto(t *testing.T) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
cluster_api "github.com/pomerium/zero-sdk/cluster"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
connect_mux "github.com/pomerium/zero-sdk/connect-mux"
|
||||
connect_mux "github.com/pomerium/pomerium/pkg/zero/connect-mux"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
|
|
116
pkg/fanout/config.go
Normal file
116
pkg/fanout/config.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package fanout
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
defaultPublishTimeout = time.Second
|
||||
minPublishTimeout = time.Millisecond * 100
|
||||
|
||||
defaultReceiverCallbackTimeout = time.Second
|
||||
minReceiverCallbackTimeout = time.Millisecond * 100
|
||||
|
||||
defaultAddSubscriberTimeout = time.Millisecond * 100
|
||||
minAddSubscriberTimeout = time.Millisecond * 100
|
||||
|
||||
defaultReceiverBufferSize = 100
|
||||
defaultMessageBufferSize = 1024
|
||||
defaultSubscriberBufferSize = 100
|
||||
)
|
||||
|
||||
type config struct {
|
||||
publishTimeout time.Duration
|
||||
receiverBufferSize int
|
||||
receiverCallbackTimeout time.Duration
|
||||
publishBufferSize int
|
||||
subscriberBufferSize int
|
||||
addSubscriberTimeout time.Duration
|
||||
}
|
||||
|
||||
// Option configures a FanOut
|
||||
type Option func(*config)
|
||||
|
||||
// WithPublishTimeout sets the internal timeout for publishing messages to the fanout
|
||||
func WithPublishTimeout(timeout time.Duration) Option {
|
||||
if timeout < defaultPublishTimeout {
|
||||
timeout = defaultPublishTimeout
|
||||
}
|
||||
|
||||
return func(c *config) {
|
||||
c.publishTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// WithReceiverBufferSize sets the buffer size for the buffer between fanout and subscriber receiver
|
||||
func WithReceiverBufferSize(size int) Option {
|
||||
if size < 1 {
|
||||
size = 1
|
||||
}
|
||||
return func(c *config) {
|
||||
c.receiverBufferSize = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithReceiverCallbackTimeout sets the timeout for the callback function of the receiver
|
||||
func WithReceiverCallbackTimeout(timeout time.Duration) Option {
|
||||
if timeout < minReceiverCallbackTimeout {
|
||||
timeout = minReceiverCallbackTimeout
|
||||
}
|
||||
|
||||
return func(c *config) {
|
||||
c.receiverCallbackTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagesBufferSize sets the buffer size for the buffer that holds messages to be published
|
||||
func WithMessagesBufferSize(size int) Option {
|
||||
if size < 1 {
|
||||
size = 1
|
||||
}
|
||||
return func(c *config) {
|
||||
c.publishBufferSize = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithSubscriberBufferSize sets the new subscriber requsts buffer size
|
||||
func WithSubscriberBufferSize(size int) Option {
|
||||
if size < 1 {
|
||||
size = 1
|
||||
}
|
||||
return func(c *config) {
|
||||
c.subscriberBufferSize = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithAddSubscriberTimeout sets the timeout for adding a subscriber
|
||||
// If it is not possible to add a subscriber within the timeout,
|
||||
// it means the fanout is at capacity, and it is better to reject the subscriber,
|
||||
// that will likely be propagated to the downstream,
|
||||
// which would retry and eventually succeed with another instance
|
||||
func WithAddSubscriberTimeout(timeout time.Duration) Option {
|
||||
if timeout < minAddSubscriberTimeout {
|
||||
timeout = minAddSubscriberTimeout
|
||||
}
|
||||
|
||||
return func(c *config) {
|
||||
c.addSubscriberTimeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func defaultFanOutConfig() config {
|
||||
var c config
|
||||
c.apply(
|
||||
WithPublishTimeout(defaultPublishTimeout),
|
||||
WithMessagesBufferSize(defaultMessageBufferSize),
|
||||
WithReceiverCallbackTimeout(defaultReceiverCallbackTimeout),
|
||||
WithReceiverBufferSize(defaultReceiverBufferSize),
|
||||
WithSubscriberBufferSize(defaultSubscriberBufferSize),
|
||||
WithAddSubscriberTimeout(defaultAddSubscriberTimeout),
|
||||
)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *config) apply(opts ...Option) {
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
}
|
70
pkg/fanout/fanout.go
Normal file
70
pkg/fanout/fanout.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Package fanout implements a fan-out pattern that allows publishing messages to multiple subscribers
|
||||
package fanout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrSubscriberClosed is returned when a subscriber is closed on the subscriber side (Receive)
|
||||
ErrSubscriberClosed = errors.New("subscriber closed")
|
||||
// ErrSubscriberEvicted is returned when a subscriber is unable to keep up with the messages
|
||||
ErrSubscriberEvicted = errors.New("subscriber evicted, cannot keep up consuming messages")
|
||||
// ErrStopped is returned when the fanout is stopped
|
||||
ErrStopped = errors.New("fanout is stopped, no more messages will be accepted")
|
||||
)
|
||||
|
||||
// FanOut is a fan-out pattern implementation that allows publishing messages to multiple subscribers
|
||||
type FanOut[T any] struct {
|
||||
cfg config
|
||||
done <-chan struct{}
|
||||
messages chan T
|
||||
subscribers chan *subscriber[T]
|
||||
}
|
||||
|
||||
// Start creates and runs a new FanOut
|
||||
func Start[T any](ctx context.Context, opts ...Option) *FanOut[T] {
|
||||
cfg := defaultFanOutConfig()
|
||||
cfg.apply(opts...)
|
||||
|
||||
f := &FanOut[T]{
|
||||
cfg: cfg,
|
||||
done: ctx.Done(),
|
||||
messages: make(chan T, cfg.publishBufferSize),
|
||||
subscribers: make(chan *subscriber[T], cfg.subscriberBufferSize),
|
||||
}
|
||||
go f.dispatchLoop(ctx)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *FanOut[T]) dispatchLoop(ctx context.Context) {
|
||||
subscribers := make(subscribers[T])
|
||||
defer subscribers.closeAll(ErrStopped)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case sub := <-f.subscribers:
|
||||
subscribers.add(sub)
|
||||
continue
|
||||
case msg := <-f.messages:
|
||||
subscribers.dispatch(ctx, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FanOut[T]) addSubscriber(ctx context.Context, sub *subscriber[T]) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, f.cfg.addSubscriberTimeout)
|
||||
defer cancel()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return context.Cause(ctx)
|
||||
case <-f.done:
|
||||
return ErrStopped
|
||||
case f.subscribers <- sub:
|
||||
return nil
|
||||
}
|
||||
}
|
182
pkg/fanout/fanout_test.go
Normal file
182
pkg/fanout/fanout_test.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
package fanout_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/fanout"
|
||||
)
|
||||
|
||||
func TestFanOutStopped(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
f := fanout.Start[int](ctx, fanout.WithPublishTimeout(time.Millisecond*10))
|
||||
assert.Eventually(t, func() bool {
|
||||
return errors.Is(f.Publish(context.Background(), 1), fanout.ErrStopped)
|
||||
}, 5*time.Second, 10*time.Millisecond)
|
||||
|
||||
err := f.Receive(context.Background(), func(ctx context.Context, msg int) error {
|
||||
return nil
|
||||
})
|
||||
assert.ErrorIs(t, err, fanout.ErrStopped)
|
||||
}
|
||||
|
||||
func TestFanOutEvictSlowSubscriber(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
timeout := time.Second * 5
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
f := fanout.Start[int](ctx,
|
||||
fanout.WithReceiverBufferSize(1),
|
||||
fanout.WithReceiverCallbackTimeout(timeout),
|
||||
)
|
||||
|
||||
subscriberAdded := make(chan struct{})
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.Go(func() error {
|
||||
err := f.Receive(ctx, func(ctx context.Context, msg int) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// context was canceled as expected
|
||||
// when the subscriber was evicted
|
||||
case <-time.After(timeout / 2):
|
||||
t.Error("receiver context was not canceled")
|
||||
}
|
||||
return nil
|
||||
}, fanout.WithOnSubscriberAdded[int](func() {
|
||||
close(subscriberAdded)
|
||||
}))
|
||||
assert.ErrorIs(t, err, fanout.ErrSubscriberEvicted, "expect explicit error indicating subscriber eviction")
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("timed out waiting for subscriber: %w", ctx.Err())
|
||||
case <-subscriberAdded:
|
||||
}
|
||||
// this message will be consumed by the subscriber above, which will block in the callback
|
||||
assert.NoError(t, f.Publish(ctx, 1))
|
||||
// this message will get into fanout-receiver's buffer as the subscriber is blocked
|
||||
assert.NoError(t, f.Publish(ctx, 1))
|
||||
// this messsage will cause the subscriber to be evicted as all buffers are full
|
||||
assert.NoError(t, f.Publish(ctx, 1))
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, eg.Wait())
|
||||
}
|
||||
|
||||
func TestFanOutReceiverCancelOnError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
f := fanout.Start[int](ctx)
|
||||
receiverErr := errors.New("receiver error")
|
||||
errch := make(chan error, 1)
|
||||
|
||||
ready := make(chan struct{})
|
||||
go func() {
|
||||
errch <- f.Receive(ctx, func(ctx context.Context, msg int) error {
|
||||
return receiverErr
|
||||
}, fanout.WithOnSubscriberAdded[int](func() { close(ready) }))
|
||||
}()
|
||||
|
||||
<-ready
|
||||
require.NoError(t, f.Publish(ctx, 1))
|
||||
assert.ErrorIs(t, <-errch, receiverErr)
|
||||
}
|
||||
|
||||
func TestFanOutFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
f := fanout.Start[int](ctx)
|
||||
ready := make(chan struct{})
|
||||
results := make(chan int)
|
||||
go func() {
|
||||
_ = f.Receive(ctx, func(ctx context.Context, msg int) error {
|
||||
results <- msg
|
||||
return nil
|
||||
},
|
||||
fanout.WithFilter(func(msg int) bool { return msg%2 == 0 }),
|
||||
fanout.WithOnSubscriberAdded[int](func() { close(ready) }),
|
||||
)
|
||||
}()
|
||||
<-ready
|
||||
t.Log("ready to publish")
|
||||
for i := 0; i < 10; i++ {
|
||||
assert.NoError(t, f.Publish(ctx, i))
|
||||
}
|
||||
t.Log("published all messages")
|
||||
|
||||
for i := 0; i < 9; i += 2 {
|
||||
assert.Equal(t, i, <-results)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFanout(b *testing.B) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
|
||||
b.Cleanup(cancel)
|
||||
|
||||
cycles := 1
|
||||
|
||||
f := fanout.Start[int](ctx)
|
||||
errStopReceiver := errors.New("stop receiver")
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.SetLimit(-1)
|
||||
ready := make(chan struct{}, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
want := i
|
||||
eg.Go(func() error {
|
||||
seen := 0
|
||||
err := f.Receive(ctx, func(ctx context.Context, _ int) error {
|
||||
if seen++; seen == cycles {
|
||||
return errStopReceiver
|
||||
}
|
||||
return nil
|
||||
},
|
||||
fanout.WithOnSubscriberAdded[int](func() { ready <- struct{}{} }),
|
||||
fanout.WithFilter(func(msg int) bool { return msg == want }),
|
||||
)
|
||||
if !errors.Is(err, errStopReceiver) && !errors.Is(err, context.Canceled) {
|
||||
b.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
eg.Go(func() error {
|
||||
for i := 0; i < b.N; i++ {
|
||||
<-ready
|
||||
}
|
||||
|
||||
for c := 0; c < cycles; c++ {
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := f.Publish(ctx, i)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(b, eg.Wait())
|
||||
}
|
19
pkg/fanout/publish.go
Normal file
19
pkg/fanout/publish.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package fanout
|
||||
|
||||
import "context"
|
||||
|
||||
// Publish publishes a message to all currently registered subscribers
|
||||
// if the fanout is closed, ErrStopped is returned
|
||||
func (f *FanOut[T]) Publish(ctx context.Context, msg T) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, f.cfg.publishTimeout)
|
||||
defer cancel()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-f.done:
|
||||
return ErrStopped
|
||||
case f.messages <- msg:
|
||||
return nil
|
||||
}
|
||||
}
|
106
pkg/fanout/receive.go
Normal file
106
pkg/fanout/receive.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package fanout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReceiverCallback is the callback function that is called for each message received
|
||||
// if an error is returned, Receive will return immediately with that error, closing the subscriber
|
||||
type ReceiverCallback[T any] func(ctx context.Context, msg T) error
|
||||
|
||||
// ReceiveOption is an option for receiver
|
||||
type ReceiveOption[T any] func(*subscriber[T])
|
||||
|
||||
// WithFilter returns a ReceiveOption that filters messages for the subscriber
|
||||
// if the filter returns false, the message is not sent to the subscriber
|
||||
// this function is called for each message received and subsequently for each subscriber
|
||||
// and should not be computationally expensive or block
|
||||
func WithFilter[T any](filter func(T) bool) ReceiveOption[T] {
|
||||
return func(sub *subscriber[T]) {
|
||||
sub.filter = filter
|
||||
}
|
||||
}
|
||||
|
||||
// WithOnSubscriberAdded should only be used for tests
|
||||
func WithOnSubscriberAdded[T any](onAdded func()) ReceiveOption[T] {
|
||||
return func(sub *subscriber[T]) {
|
||||
sub.onAdded = onAdded
|
||||
}
|
||||
}
|
||||
|
||||
// Receive subscribes to receive messages until the context is canceled or an error occurs
|
||||
// onMessage is called for each message received.
|
||||
// if an error is returned, Receive will return immediately
|
||||
func (f *FanOut[T]) Receive(ctx context.Context, onMessage ReceiverCallback[T], opts ...ReceiveOption[T]) error {
|
||||
ctx, cancel := context.WithCancelCause(ctx)
|
||||
defer cancel(nil)
|
||||
|
||||
messages := make(chan T, f.cfg.receiverBufferSize)
|
||||
sub := newSubscriber[T](messages, f.done, cancel, opts...)
|
||||
|
||||
err := f.addSubscriber(ctx, sub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add subscriber: %w", err)
|
||||
}
|
||||
|
||||
err = f.receiveLoop(ctx, messages, onMessage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("receive: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newSubscriber[T any](
|
||||
messages chan<- T,
|
||||
done <-chan struct{},
|
||||
cancel context.CancelCauseFunc,
|
||||
opts ...ReceiveOption[T],
|
||||
) *subscriber[T] {
|
||||
sub := &subscriber[T]{
|
||||
messages: messages,
|
||||
done: done,
|
||||
cancel: cancel,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(sub)
|
||||
}
|
||||
return sub
|
||||
}
|
||||
|
||||
func (f *FanOut[T]) receiveLoop(
|
||||
ctx context.Context,
|
||||
messages <-chan T,
|
||||
onMessage ReceiverCallback[T],
|
||||
) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return context.Cause(ctx)
|
||||
case <-f.done:
|
||||
return ErrStopped
|
||||
case msg, ok := <-messages:
|
||||
if !ok {
|
||||
return ErrSubscriberEvicted
|
||||
}
|
||||
err := callWithTimeout(ctx, f.cfg.receiverCallbackTimeout, onMessage, msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("onMessage callback: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callWithTimeout[T any](
|
||||
ctx context.Context,
|
||||
timeout time.Duration,
|
||||
cb ReceiverCallback[T],
|
||||
msg T,
|
||||
) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
return cb(ctx, msg)
|
||||
}
|
65
pkg/fanout/subscribers.go
Normal file
65
pkg/fanout/subscribers.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package fanout
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// subscriber represents an individual subscriber to a fanout
|
||||
type subscriber[T any] struct {
|
||||
// messages is the channel that the subscriber receives messages on
|
||||
messages chan<- T
|
||||
// done is closed when the subscriber is closed
|
||||
// it is used to signal the dispatchLoop that the subscriber is closed and should be removed
|
||||
done <-chan struct{}
|
||||
// cancel is passed so that dispatchLoop can cancel the subscriber if it discards it from its side
|
||||
cancel context.CancelCauseFunc
|
||||
// filter identifies the messages that the subscriber is interested in.
|
||||
filter func(T) bool
|
||||
// onAdded is called when the subscriber is added to the fanout
|
||||
// it is only used for tests
|
||||
onAdded func()
|
||||
}
|
||||
|
||||
// subscriberCloseFn is a function that closes a subscriber and propagates an error to it
|
||||
type subscriberCloseFn func(err error)
|
||||
|
||||
// subscribers is a map of subscribers to their close functions
|
||||
type subscribers[T any] map[*subscriber[T]]subscriberCloseFn
|
||||
|
||||
// closeAll closes all subscribers and propagates the given error to them
|
||||
func (s subscribers[T]) closeAll(err error) {
|
||||
for _, close := range s {
|
||||
close(err)
|
||||
}
|
||||
}
|
||||
|
||||
// add adds subscriber to the fanout
|
||||
func (s subscribers[T]) add(sub *subscriber[T]) {
|
||||
s[sub] = func(err error) {
|
||||
close(sub.messages)
|
||||
sub.cancel(err)
|
||||
delete(s, sub)
|
||||
}
|
||||
if sub.onAdded != nil {
|
||||
sub.onAdded()
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch dispatches the given message to all subscribers
|
||||
func (s subscribers[T]) dispatch(ctx context.Context, msg T) {
|
||||
for sub, close := range s {
|
||||
if sub.filter != nil && !sub.filter(msg) {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case sub.messages <- msg:
|
||||
case <-sub.done:
|
||||
close(ErrSubscriberClosed)
|
||||
default:
|
||||
close(ErrSubscriberEvicted)
|
||||
}
|
||||
}
|
||||
}
|
123
pkg/zero/api.go
Normal file
123
pkg/zero/api.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Package zero contains the pomerium zero configuration API client
|
||||
package zero
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/fanout"
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
connect_api "github.com/pomerium/pomerium/pkg/zero/connect"
|
||||
connect_mux "github.com/pomerium/pomerium/pkg/zero/connect-mux"
|
||||
token_api "github.com/pomerium/pomerium/pkg/zero/token"
|
||||
)
|
||||
|
||||
// API is a Pomerium Zero Cluster API client
|
||||
type API struct {
|
||||
cfg *config
|
||||
cluster cluster_api.ClientWithResponsesInterface
|
||||
mux *connect_mux.Mux
|
||||
downloadURLCache *cluster_api.URLCache
|
||||
}
|
||||
|
||||
// WatchOption defines which events to watch for
|
||||
type WatchOption = connect_mux.WatchOption
|
||||
|
||||
// NewAPI creates a new API client
|
||||
func NewAPI(ctx context.Context, opts ...Option) (*API, error) {
|
||||
cfg, err := newConfig(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fetcher, err := cluster_api.NewTokenFetcher(cfg.clusterAPIEndpoint,
|
||||
cluster_api.WithHTTPClient(cfg.httpClient),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating token fetcher: %w", err)
|
||||
}
|
||||
|
||||
tokenCache := token_api.NewCache(fetcher, cfg.apiToken)
|
||||
|
||||
clusterClient, err := cluster_api.NewAuthorizedClient(cfg.clusterAPIEndpoint, tokenCache.GetToken, cfg.httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating cluster client: %w", err)
|
||||
}
|
||||
|
||||
connectClient, err := connect_api.NewAuthorizedConnectClient(ctx, cfg.connectAPIEndpoint, tokenCache.GetToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating connect client: %w", err)
|
||||
}
|
||||
|
||||
return &API{
|
||||
cfg: cfg,
|
||||
cluster: clusterClient,
|
||||
mux: connect_mux.New(connectClient),
|
||||
downloadURLCache: cluster_api.NewURLCache(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Connect connects to the connect API and allows watching for changes
|
||||
func (api *API) Connect(ctx context.Context, opts ...fanout.Option) error {
|
||||
return api.mux.Run(ctx, opts...)
|
||||
}
|
||||
|
||||
// Watch dispatches API updates
|
||||
func (api *API) Watch(ctx context.Context, opts ...WatchOption) error {
|
||||
return api.mux.Watch(ctx, opts...)
|
||||
}
|
||||
|
||||
// GetClusterBootstrapConfig fetches the bootstrap configuration from the cluster API
|
||||
func (api *API) GetClusterBootstrapConfig(ctx context.Context) (*cluster_api.BootstrapConfig, error) {
|
||||
return apierror.CheckResponse[cluster_api.BootstrapConfig](
|
||||
api.cluster.GetClusterBootstrapConfigWithResponse(ctx),
|
||||
)
|
||||
}
|
||||
|
||||
// GetClusterResourceBundles fetches the resource bundles from the cluster API
|
||||
func (api *API) GetClusterResourceBundles(ctx context.Context) (*cluster_api.GetBundlesResponse, error) {
|
||||
return apierror.CheckResponse[cluster_api.GetBundlesResponse](
|
||||
api.cluster.GetClusterResourceBundlesWithResponse(ctx),
|
||||
)
|
||||
}
|
||||
|
||||
// ReportBundleAppliedSuccess reports a successful bundle application
|
||||
func (api *API) ReportBundleAppliedSuccess(ctx context.Context, bundleID string, metadata map[string]string) error {
|
||||
status := cluster_api.BundleStatus{
|
||||
Success: &cluster_api.BundleStatusSuccess{
|
||||
Metadata: metadata,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := apierror.CheckResponse[cluster_api.EmptyResponse](
|
||||
api.cluster.ReportClusterResourceBundleStatusWithResponse(ctx, bundleID, status),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reporting bundle status: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ReportBundleAppliedFailure reports a failed bundle application
|
||||
func (api *API) ReportBundleAppliedFailure(
|
||||
ctx context.Context,
|
||||
bundleID string,
|
||||
source cluster_api.BundleStatusFailureSource,
|
||||
err error,
|
||||
) error {
|
||||
status := cluster_api.BundleStatus{
|
||||
Failure: &cluster_api.BundleStatusFailure{
|
||||
Message: err.Error(),
|
||||
Source: source,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = apierror.CheckResponse[cluster_api.EmptyResponse](
|
||||
api.cluster.ReportClusterResourceBundleStatusWithResponse(ctx, bundleID, status),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reporting bundle status: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
42
pkg/zero/apierror/request_id.go
Normal file
42
pkg/zero/apierror/request_id.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package apierror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RequestIDError is an error that wraps another error and includes the response ID
|
||||
type RequestIDError struct {
|
||||
Err error
|
||||
ResponseID *string
|
||||
}
|
||||
|
||||
// Error implements error for RequestIDError
|
||||
func (e *RequestIDError) Error() string {
|
||||
if e.ResponseID == nil {
|
||||
return e.Err.Error()
|
||||
}
|
||||
return fmt.Sprintf("[x-response-id:%s]: %v", *e.ResponseID, e.Err)
|
||||
}
|
||||
|
||||
// Unwrap implements errors.Unwrap for RequestIDError
|
||||
func (e *RequestIDError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Is implements errors.Is for RequestIDError
|
||||
func (e *RequestIDError) Is(err error) bool {
|
||||
//nolint:errorlint
|
||||
_, ok := err.(*RequestIDError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// WithRequestID creates a new RequestIDError
|
||||
func WithRequestID(err error, headers http.Header) *RequestIDError {
|
||||
r := &RequestIDError{Err: err}
|
||||
id := headers.Get("X-Response-Id")
|
||||
if id != "" {
|
||||
r.ResponseID = &id
|
||||
}
|
||||
return r
|
||||
}
|
56
pkg/zero/apierror/response.go
Normal file
56
pkg/zero/apierror/response.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Package apierror provides a consistent way to handle errors from API calls
|
||||
package apierror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CheckResponse checks the response for errors and returns the value or an error
|
||||
func CheckResponse[T any](resp APIResponse[T], err error) (*T, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value := resp.GetValue()
|
||||
if value != nil {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
//nolint:bodyclose
|
||||
return nil, WithRequestID(responseError(resp), resp.GetHTTPResponse().Header)
|
||||
}
|
||||
|
||||
// APIResponse is the interface that wraps the response from an API call
|
||||
type APIResponse[T any] interface {
|
||||
// GetHTTPResponse returns the HTTP response
|
||||
GetHTTPResponse() *http.Response
|
||||
// GetInternalServerError returns the internal server error
|
||||
GetInternalServerError() (string, bool)
|
||||
// GetBadRequestError returns the bad request error
|
||||
GetBadRequestError() (string, bool)
|
||||
// GetValue returns the value
|
||||
GetValue() *T
|
||||
}
|
||||
|
||||
// Error is the interface that wraps the error returned from an API call
|
||||
type Error interface {
|
||||
GetError() string
|
||||
}
|
||||
|
||||
func responseError[T any](resp APIResponse[T]) error {
|
||||
reason, ok := resp.GetBadRequestError()
|
||||
if ok {
|
||||
return NewTerminalError(fmt.Errorf("bad request: %v", reason))
|
||||
}
|
||||
reason, ok = resp.GetInternalServerError()
|
||||
if ok {
|
||||
return fmt.Errorf("internal server error: %v", reason)
|
||||
}
|
||||
//nolint:bodyclose
|
||||
httpResp := resp.GetHTTPResponse()
|
||||
if httpResp == nil {
|
||||
return fmt.Errorf("unexpected response: nil")
|
||||
}
|
||||
return fmt.Errorf("unexpected response: %v", httpResp.StatusCode)
|
||||
}
|
40
pkg/zero/apierror/response_test.go
Normal file
40
pkg/zero/apierror/response_test.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package apierror_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
"github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
)
|
||||
|
||||
func TestResponse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
err error
|
||||
response apierror.APIResponse[cluster.ExchangeTokenResponse]
|
||||
wantVal *cluster.ExchangeTokenResponse
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
response: &cluster.ExchangeClusterIdentityTokenResp{
|
||||
HTTPResponse: &http.Response{},
|
||||
JSON200: &cluster.ExchangeTokenResponse{},
|
||||
},
|
||||
err: nil,
|
||||
wantVal: &cluster.ExchangeTokenResponse{},
|
||||
wantErr: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
gotVal, gotErr := apierror.CheckResponse(tc.response, tc.err)
|
||||
assert.Equal(t, tc.wantVal, gotVal)
|
||||
assert.Equal(t, tc.wantErr, gotErr)
|
||||
})
|
||||
}
|
||||
}
|
44
pkg/zero/apierror/terminal.go
Normal file
44
pkg/zero/apierror/terminal.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package apierror
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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 {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var te *terminalError
|
||||
return errors.As(err, &te)
|
||||
}
|
813
pkg/zero/cluster/client.gen.go
Normal file
813
pkg/zero/cluster/client.gen.go
Normal file
|
@ -0,0 +1,813 @@
|
|||
// Package cluster provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/deepmap/oapi-codegen version v1.16.2 DO NOT EDIT.
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/oapi-codegen/runtime"
|
||||
)
|
||||
|
||||
// RequestEditorFn is the function signature for the RequestEditor callback function
|
||||
type RequestEditorFn func(ctx context.Context, req *http.Request) error
|
||||
|
||||
// Doer performs HTTP requests.
|
||||
//
|
||||
// The standard http.Client implements this interface.
|
||||
type HttpRequestDoer interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// Client which conforms to the OpenAPI3 specification for this service.
|
||||
type Client struct {
|
||||
// The endpoint of the server conforming to this interface, with scheme,
|
||||
// https://api.deepmap.com for example. This can contain a path relative
|
||||
// to the server, such as https://api.deepmap.com/dev-test, and all the
|
||||
// paths in the swagger spec will be appended to the server.
|
||||
Server string
|
||||
|
||||
// Doer for performing requests, typically a *http.Client with any
|
||||
// customized settings, such as certificate chains.
|
||||
Client HttpRequestDoer
|
||||
|
||||
// A list of callbacks for modifying requests which are generated before sending over
|
||||
// the network.
|
||||
RequestEditors []RequestEditorFn
|
||||
}
|
||||
|
||||
// ClientOption allows setting custom parameters during construction
|
||||
type ClientOption func(*Client) error
|
||||
|
||||
// Creates a new Client, with reasonable defaults
|
||||
func NewClient(server string, opts ...ClientOption) (*Client, error) {
|
||||
// create a client with sane default values
|
||||
client := Client{
|
||||
Server: server,
|
||||
}
|
||||
// mutate client and add all optional params
|
||||
for _, o := range opts {
|
||||
if err := o(&client); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// ensure the server URL always has a trailing slash
|
||||
if !strings.HasSuffix(client.Server, "/") {
|
||||
client.Server += "/"
|
||||
}
|
||||
// create httpClient, if not already present
|
||||
if client.Client == nil {
|
||||
client.Client = &http.Client{}
|
||||
}
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// WithHTTPClient allows overriding the default Doer, which is
|
||||
// automatically created using http.Client. This is useful for tests.
|
||||
func WithHTTPClient(doer HttpRequestDoer) ClientOption {
|
||||
return func(c *Client) error {
|
||||
c.Client = doer
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithRequestEditorFn allows setting up a callback function, which will be
|
||||
// called right before sending the request. This can be used to mutate the request.
|
||||
func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
|
||||
return func(c *Client) error {
|
||||
c.RequestEditors = append(c.RequestEditors, fn)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The interface specification for the client above.
|
||||
type ClientInterface interface {
|
||||
// GetClusterBootstrapConfig request
|
||||
GetClusterBootstrapConfig(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// GetClusterResourceBundles request
|
||||
GetClusterResourceBundles(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// DownloadClusterResourceBundle request
|
||||
DownloadClusterResourceBundle(ctx context.Context, bundleId BundleId, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// ReportClusterResourceBundleStatusWithBody request with any body
|
||||
ReportClusterResourceBundleStatusWithBody(ctx context.Context, bundleId BundleId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
ReportClusterResourceBundleStatus(ctx context.Context, bundleId BundleId, body ReportClusterResourceBundleStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// ExchangeClusterIdentityTokenWithBody request with any body
|
||||
ExchangeClusterIdentityTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
ExchangeClusterIdentityToken(ctx context.Context, body ExchangeClusterIdentityTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
}
|
||||
|
||||
func (c *Client) GetClusterBootstrapConfig(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewGetClusterBootstrapConfigRequest(c.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) GetClusterResourceBundles(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewGetClusterResourceBundlesRequest(c.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) DownloadClusterResourceBundle(ctx context.Context, bundleId BundleId, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewDownloadClusterResourceBundleRequest(c.Server, bundleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) ReportClusterResourceBundleStatusWithBody(ctx context.Context, bundleId BundleId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewReportClusterResourceBundleStatusRequestWithBody(c.Server, bundleId, contentType, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) ReportClusterResourceBundleStatus(ctx context.Context, bundleId BundleId, body ReportClusterResourceBundleStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewReportClusterResourceBundleStatusRequest(c.Server, bundleId, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) ExchangeClusterIdentityTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewExchangeClusterIdentityTokenRequestWithBody(c.Server, contentType, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *Client) ExchangeClusterIdentityToken(ctx context.Context, body ExchangeClusterIdentityTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewExchangeClusterIdentityTokenRequest(c.Server, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
// NewGetClusterBootstrapConfigRequest generates requests for GetClusterBootstrapConfig
|
||||
func NewGetClusterBootstrapConfigRequest(server string) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/bootstrap")
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = "." + operationPath
|
||||
}
|
||||
|
||||
queryURL, err := serverURL.Parse(operationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", queryURL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewGetClusterResourceBundlesRequest generates requests for GetClusterResourceBundles
|
||||
func NewGetClusterResourceBundlesRequest(server string) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/bundles")
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = "." + operationPath
|
||||
}
|
||||
|
||||
queryURL, err := serverURL.Parse(operationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", queryURL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewDownloadClusterResourceBundleRequest generates requests for DownloadClusterResourceBundle
|
||||
func NewDownloadClusterResourceBundleRequest(server string, bundleId BundleId) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
var pathParam0 string
|
||||
|
||||
pathParam0, err = runtime.StyleParamWithLocation("simple", false, "bundleId", runtime.ParamLocationPath, bundleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/bundles/%s/download", pathParam0)
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = "." + operationPath
|
||||
}
|
||||
|
||||
queryURL, err := serverURL.Parse(operationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", queryURL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewReportClusterResourceBundleStatusRequest calls the generic ReportClusterResourceBundleStatus builder with application/json body
|
||||
func NewReportClusterResourceBundleStatusRequest(server string, bundleId BundleId, body ReportClusterResourceBundleStatusJSONRequestBody) (*http.Request, error) {
|
||||
var bodyReader io.Reader
|
||||
buf, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader = bytes.NewReader(buf)
|
||||
return NewReportClusterResourceBundleStatusRequestWithBody(server, bundleId, "application/json", bodyReader)
|
||||
}
|
||||
|
||||
// NewReportClusterResourceBundleStatusRequestWithBody generates requests for ReportClusterResourceBundleStatus with any type of body
|
||||
func NewReportClusterResourceBundleStatusRequestWithBody(server string, bundleId BundleId, contentType string, body io.Reader) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
var pathParam0 string
|
||||
|
||||
pathParam0, err = runtime.StyleParamWithLocation("simple", false, "bundleId", runtime.ParamLocationPath, bundleId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/bundles/%s/status", pathParam0)
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = "." + operationPath
|
||||
}
|
||||
|
||||
queryURL, err := serverURL.Parse(operationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", queryURL.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", contentType)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewExchangeClusterIdentityTokenRequest calls the generic ExchangeClusterIdentityToken builder with application/json body
|
||||
func NewExchangeClusterIdentityTokenRequest(server string, body ExchangeClusterIdentityTokenJSONRequestBody) (*http.Request, error) {
|
||||
var bodyReader io.Reader
|
||||
buf, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader = bytes.NewReader(buf)
|
||||
return NewExchangeClusterIdentityTokenRequestWithBody(server, "application/json", bodyReader)
|
||||
}
|
||||
|
||||
// NewExchangeClusterIdentityTokenRequestWithBody generates requests for ExchangeClusterIdentityToken with any type of body
|
||||
func NewExchangeClusterIdentityTokenRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/exchangeToken")
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = "." + operationPath
|
||||
}
|
||||
|
||||
queryURL, err := serverURL.Parse(operationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", queryURL.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", contentType)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
|
||||
for _, r := range c.RequestEditors {
|
||||
if err := r(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, r := range additionalEditors {
|
||||
if err := r(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClientWithResponses builds on ClientInterface to offer response payloads
|
||||
type ClientWithResponses struct {
|
||||
ClientInterface
|
||||
}
|
||||
|
||||
// NewClientWithResponses creates a new ClientWithResponses, which wraps
|
||||
// Client with return type handling
|
||||
func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
|
||||
client, err := NewClient(server, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ClientWithResponses{client}, nil
|
||||
}
|
||||
|
||||
// WithBaseURL overrides the baseURL.
|
||||
func WithBaseURL(baseURL string) ClientOption {
|
||||
return func(c *Client) error {
|
||||
newBaseURL, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Server = newBaseURL.String()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ClientWithResponsesInterface is the interface specification for the client with responses above.
|
||||
type ClientWithResponsesInterface interface {
|
||||
// GetClusterBootstrapConfigWithResponse request
|
||||
GetClusterBootstrapConfigWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClusterBootstrapConfigResp, error)
|
||||
|
||||
// GetClusterResourceBundlesWithResponse request
|
||||
GetClusterResourceBundlesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClusterResourceBundlesResp, error)
|
||||
|
||||
// DownloadClusterResourceBundleWithResponse request
|
||||
DownloadClusterResourceBundleWithResponse(ctx context.Context, bundleId BundleId, reqEditors ...RequestEditorFn) (*DownloadClusterResourceBundleResp, error)
|
||||
|
||||
// ReportClusterResourceBundleStatusWithBodyWithResponse request with any body
|
||||
ReportClusterResourceBundleStatusWithBodyWithResponse(ctx context.Context, bundleId BundleId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ReportClusterResourceBundleStatusResp, error)
|
||||
|
||||
ReportClusterResourceBundleStatusWithResponse(ctx context.Context, bundleId BundleId, body ReportClusterResourceBundleStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*ReportClusterResourceBundleStatusResp, error)
|
||||
|
||||
// ExchangeClusterIdentityTokenWithBodyWithResponse request with any body
|
||||
ExchangeClusterIdentityTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ExchangeClusterIdentityTokenResp, error)
|
||||
|
||||
ExchangeClusterIdentityTokenWithResponse(ctx context.Context, body ExchangeClusterIdentityTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*ExchangeClusterIdentityTokenResp, error)
|
||||
}
|
||||
|
||||
type GetClusterBootstrapConfigResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON200 *GetBootstrapConfigResponse
|
||||
JSON400 *ErrorResponse
|
||||
JSON500 *ErrorResponse
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r GetClusterBootstrapConfigResp) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r GetClusterBootstrapConfigResp) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetClusterResourceBundlesResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON200 *GetBundlesResponse
|
||||
JSON400 *ErrorResponse
|
||||
JSON500 *ErrorResponse
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r GetClusterResourceBundlesResp) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r GetClusterResourceBundlesResp) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundleResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON200 *DownloadBundleResponse
|
||||
JSON400 *ErrorResponse
|
||||
JSON404 *ErrorResponse
|
||||
JSON500 *ErrorResponse
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r DownloadClusterResourceBundleResp) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r DownloadClusterResourceBundleResp) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ReportClusterResourceBundleStatusResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON400 *ErrorResponse
|
||||
JSON500 *ErrorResponse
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r ReportClusterResourceBundleStatusResp) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r ReportClusterResourceBundleStatusResp) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ExchangeClusterIdentityTokenResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON200 *ExchangeTokenResponse
|
||||
JSON400 *ErrorResponse
|
||||
JSON500 *ErrorResponse
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r ExchangeClusterIdentityTokenResp) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r ExchangeClusterIdentityTokenResp) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetClusterBootstrapConfigWithResponse request returning *GetClusterBootstrapConfigResp
|
||||
func (c *ClientWithResponses) GetClusterBootstrapConfigWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClusterBootstrapConfigResp, error) {
|
||||
rsp, err := c.GetClusterBootstrapConfig(ctx, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseGetClusterBootstrapConfigResp(rsp)
|
||||
}
|
||||
|
||||
// GetClusterResourceBundlesWithResponse request returning *GetClusterResourceBundlesResp
|
||||
func (c *ClientWithResponses) GetClusterResourceBundlesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClusterResourceBundlesResp, error) {
|
||||
rsp, err := c.GetClusterResourceBundles(ctx, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseGetClusterResourceBundlesResp(rsp)
|
||||
}
|
||||
|
||||
// DownloadClusterResourceBundleWithResponse request returning *DownloadClusterResourceBundleResp
|
||||
func (c *ClientWithResponses) DownloadClusterResourceBundleWithResponse(ctx context.Context, bundleId BundleId, reqEditors ...RequestEditorFn) (*DownloadClusterResourceBundleResp, error) {
|
||||
rsp, err := c.DownloadClusterResourceBundle(ctx, bundleId, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseDownloadClusterResourceBundleResp(rsp)
|
||||
}
|
||||
|
||||
// ReportClusterResourceBundleStatusWithBodyWithResponse request with arbitrary body returning *ReportClusterResourceBundleStatusResp
|
||||
func (c *ClientWithResponses) ReportClusterResourceBundleStatusWithBodyWithResponse(ctx context.Context, bundleId BundleId, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ReportClusterResourceBundleStatusResp, error) {
|
||||
rsp, err := c.ReportClusterResourceBundleStatusWithBody(ctx, bundleId, contentType, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseReportClusterResourceBundleStatusResp(rsp)
|
||||
}
|
||||
|
||||
func (c *ClientWithResponses) ReportClusterResourceBundleStatusWithResponse(ctx context.Context, bundleId BundleId, body ReportClusterResourceBundleStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*ReportClusterResourceBundleStatusResp, error) {
|
||||
rsp, err := c.ReportClusterResourceBundleStatus(ctx, bundleId, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseReportClusterResourceBundleStatusResp(rsp)
|
||||
}
|
||||
|
||||
// ExchangeClusterIdentityTokenWithBodyWithResponse request with arbitrary body returning *ExchangeClusterIdentityTokenResp
|
||||
func (c *ClientWithResponses) ExchangeClusterIdentityTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ExchangeClusterIdentityTokenResp, error) {
|
||||
rsp, err := c.ExchangeClusterIdentityTokenWithBody(ctx, contentType, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseExchangeClusterIdentityTokenResp(rsp)
|
||||
}
|
||||
|
||||
func (c *ClientWithResponses) ExchangeClusterIdentityTokenWithResponse(ctx context.Context, body ExchangeClusterIdentityTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*ExchangeClusterIdentityTokenResp, error) {
|
||||
rsp, err := c.ExchangeClusterIdentityToken(ctx, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseExchangeClusterIdentityTokenResp(rsp)
|
||||
}
|
||||
|
||||
// ParseGetClusterBootstrapConfigResp parses an HTTP response from a GetClusterBootstrapConfigWithResponse call
|
||||
func ParseGetClusterBootstrapConfigResp(rsp *http.Response) (*GetClusterBootstrapConfigResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
defer func() { _ = rsp.Body.Close() }()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &GetClusterBootstrapConfigResp{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||
var dest GetBootstrapConfigResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON200 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON400 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON500 = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ParseGetClusterResourceBundlesResp parses an HTTP response from a GetClusterResourceBundlesWithResponse call
|
||||
func ParseGetClusterResourceBundlesResp(rsp *http.Response) (*GetClusterResourceBundlesResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
defer func() { _ = rsp.Body.Close() }()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &GetClusterResourceBundlesResp{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||
var dest GetBundlesResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON200 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON400 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON500 = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ParseDownloadClusterResourceBundleResp parses an HTTP response from a DownloadClusterResourceBundleWithResponse call
|
||||
func ParseDownloadClusterResourceBundleResp(rsp *http.Response) (*DownloadClusterResourceBundleResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
defer func() { _ = rsp.Body.Close() }()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &DownloadClusterResourceBundleResp{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||
var dest DownloadBundleResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON200 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON400 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON404 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON500 = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ParseReportClusterResourceBundleStatusResp parses an HTTP response from a ReportClusterResourceBundleStatusWithResponse call
|
||||
func ParseReportClusterResourceBundleStatusResp(rsp *http.Response) (*ReportClusterResourceBundleStatusResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
defer func() { _ = rsp.Body.Close() }()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &ReportClusterResourceBundleStatusResp{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON400 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON500 = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ParseExchangeClusterIdentityTokenResp parses an HTTP response from a ExchangeClusterIdentityTokenWithResponse call
|
||||
func ParseExchangeClusterIdentityTokenResp(rsp *http.Response) (*ExchangeClusterIdentityTokenResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
defer func() { _ = rsp.Body.Close() }()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &ExchangeClusterIdentityTokenResp{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||
var dest ExchangeTokenResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON200 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON400 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500:
|
||||
var dest ErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON500 = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
49
pkg/zero/cluster/client.go
Normal file
49
pkg/zero/cluster/client.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Package cluster is an API client for the cluster service
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMinTokenTTL = time.Minute * 5
|
||||
)
|
||||
|
||||
type client struct {
|
||||
tokenProvider TokenProviderFn
|
||||
httpClient *http.Client
|
||||
minTokenTTL time.Duration
|
||||
}
|
||||
|
||||
// TokenProviderFn is a function that returns a token that is expected to be valid for at least minTTL
|
||||
type TokenProviderFn func(ctx context.Context, minTTL time.Duration) (string, error)
|
||||
|
||||
// NewAuthorizedClient creates a new HTTP client that will automatically add an authorization header
|
||||
func NewAuthorizedClient(
|
||||
endpoint string,
|
||||
tokenProvider TokenProviderFn,
|
||||
httpClient *http.Client,
|
||||
) (ClientWithResponsesInterface, error) {
|
||||
c := &client{
|
||||
minTokenTTL: defaultMinTokenTTL,
|
||||
httpClient: httpClient,
|
||||
}
|
||||
|
||||
c.tokenProvider = tokenProvider
|
||||
|
||||
return NewClientWithResponses(endpoint, WithHTTPClient(c))
|
||||
}
|
||||
|
||||
func (c *client) Do(req *http.Request) (*http.Response, error) {
|
||||
ctx := req.Context()
|
||||
token, err := c.tokenProvider(ctx, c.minTokenTTL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting token: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
return c.httpClient.Do(req)
|
||||
}
|
10
pkg/zero/cluster/client.yaml
Normal file
10
pkg/zero/cluster/client.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
package: cluster
|
||||
generate:
|
||||
client: true
|
||||
models: false
|
||||
output: client.gen.go
|
||||
output-options:
|
||||
skip-prune: true
|
||||
# We use Response suffix internally throughout the response objects,
|
||||
# that conflicts with generated client
|
||||
response-type-suffix: Resp
|
148
pkg/zero/cluster/client_errors.go
Normal file
148
pkg/zero/cluster/client_errors.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
)
|
||||
|
||||
// EmptyResponse is an empty response
|
||||
type EmptyResponse struct{}
|
||||
|
||||
var (
|
||||
_ apierror.APIResponse[ExchangeTokenResponse] = (*ExchangeClusterIdentityTokenResp)(nil)
|
||||
_ apierror.APIResponse[BootstrapConfig] = (*GetClusterBootstrapConfigResp)(nil)
|
||||
_ apierror.APIResponse[GetBundlesResponse] = (*GetClusterResourceBundlesResp)(nil)
|
||||
_ apierror.APIResponse[DownloadBundleResponse] = (*DownloadClusterResourceBundleResp)(nil)
|
||||
_ apierror.APIResponse[EmptyResponse] = (*ReportClusterResourceBundleStatusResp)(nil)
|
||||
)
|
||||
|
||||
// GetBadRequestError implements apierror.APIResponse
|
||||
func (r *ExchangeClusterIdentityTokenResp) GetBadRequestError() (string, bool) {
|
||||
if r.JSON400 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON400.Error, true
|
||||
}
|
||||
|
||||
// GetInternalServerError implements apierror.APIResponse
|
||||
func (r *ExchangeClusterIdentityTokenResp) GetInternalServerError() (string, bool) {
|
||||
if r.JSON500 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON500.Error, true
|
||||
}
|
||||
|
||||
// GetValue implements apierror.APIResponse
|
||||
func (r *ExchangeClusterIdentityTokenResp) GetValue() *ExchangeTokenResponse {
|
||||
return r.JSON200
|
||||
}
|
||||
|
||||
// GetHTTPResponse implements apierror.APIResponse
|
||||
func (r *ExchangeClusterIdentityTokenResp) GetHTTPResponse() *http.Response {
|
||||
return r.HTTPResponse
|
||||
}
|
||||
|
||||
// GetBadRequestError implements apierror.APIResponse
|
||||
func (r *GetClusterBootstrapConfigResp) GetBadRequestError() (string, bool) {
|
||||
if r.JSON400 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON400.Error, true
|
||||
}
|
||||
|
||||
// GetInternalServerError implements apierror.APIResponse
|
||||
func (r *GetClusterBootstrapConfigResp) GetInternalServerError() (string, bool) {
|
||||
if r.JSON500 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON500.Error, true
|
||||
}
|
||||
|
||||
// GetValue implements apierror.APIResponse
|
||||
func (r *GetClusterBootstrapConfigResp) GetValue() *BootstrapConfig {
|
||||
return r.JSON200
|
||||
}
|
||||
|
||||
// GetHTTPResponse implements apierror.APIResponse
|
||||
func (r *GetClusterBootstrapConfigResp) GetHTTPResponse() *http.Response {
|
||||
return r.HTTPResponse
|
||||
}
|
||||
|
||||
// GetBadRequestError implements apierror.APIResponse
|
||||
func (r *GetClusterResourceBundlesResp) GetBadRequestError() (string, bool) {
|
||||
if r.JSON400 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON400.Error, true
|
||||
}
|
||||
|
||||
// GetInternalServerError implements apierror.APIResponse
|
||||
func (r *GetClusterResourceBundlesResp) GetInternalServerError() (string, bool) {
|
||||
if r.JSON500 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON500.Error, true
|
||||
}
|
||||
|
||||
// GetValue implements apierror.APIResponse
|
||||
func (r *GetClusterResourceBundlesResp) GetValue() *GetBundlesResponse {
|
||||
return r.JSON200
|
||||
}
|
||||
|
||||
// GetHTTPResponse implements apierror.APIResponse
|
||||
func (r *GetClusterResourceBundlesResp) GetHTTPResponse() *http.Response {
|
||||
return r.HTTPResponse
|
||||
}
|
||||
|
||||
// GetBadRequestError implements apierror.APIResponse
|
||||
func (r *DownloadClusterResourceBundleResp) GetBadRequestError() (string, bool) {
|
||||
if r.JSON400 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON400.Error, true
|
||||
}
|
||||
|
||||
// GetInternalServerError implements apierror.APIResponse
|
||||
func (r *DownloadClusterResourceBundleResp) GetInternalServerError() (string, bool) {
|
||||
if r.JSON500 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON500.Error, true
|
||||
}
|
||||
|
||||
// GetValue implements apierror.APIResponse
|
||||
func (r *DownloadClusterResourceBundleResp) GetValue() *DownloadBundleResponse {
|
||||
return r.JSON200
|
||||
}
|
||||
|
||||
// GetHTTPResponse implements apierror.APIResponse
|
||||
func (r *DownloadClusterResourceBundleResp) GetHTTPResponse() *http.Response {
|
||||
return r.HTTPResponse
|
||||
}
|
||||
|
||||
// GetBadRequestError implements apierror.APIResponse
|
||||
func (r *ReportClusterResourceBundleStatusResp) GetBadRequestError() (string, bool) {
|
||||
if r.JSON400 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON400.Error, true
|
||||
}
|
||||
|
||||
// GetInternalServerError implements apierror.APIResponse
|
||||
func (r *ReportClusterResourceBundleStatusResp) GetInternalServerError() (string, bool) {
|
||||
if r.JSON500 == nil {
|
||||
return "", false
|
||||
}
|
||||
return r.JSON500.Error, true
|
||||
}
|
||||
|
||||
// GetValue implements apierror.APIResponse
|
||||
func (r *ReportClusterResourceBundleStatusResp) GetValue() *EmptyResponse {
|
||||
return &EmptyResponse{}
|
||||
}
|
||||
|
||||
// GetHTTPResponse implements apierror.APIResponse
|
||||
func (r *ReportClusterResourceBundleStatusResp) GetHTTPResponse() *http.Response {
|
||||
return r.HTTPResponse
|
||||
}
|
59
pkg/zero/cluster/client_test.go
Normal file
59
pkg/zero/cluster/client_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package cluster_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
"github.com/pomerium/pomerium/pkg/zero/token"
|
||||
)
|
||||
|
||||
func TestAPIClient(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
respond := func(w http.ResponseWriter, status int, body any) {
|
||||
t.Helper()
|
||||
data, err := json.Marshal(body)
|
||||
require.NoError(t, err)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
|
||||
_, err = w.Write(data)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
idToken := "id-token"
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/exchangeToken":
|
||||
respond(w, http.StatusOK, api.ExchangeTokenResponse{
|
||||
IdToken: idToken,
|
||||
ExpiresInSeconds: "3600",
|
||||
})
|
||||
default:
|
||||
t.Error("unexpected request", r.URL.Path)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
fetcher, err := api.NewTokenFetcher(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenCache := token.NewCache(fetcher, "refresh-token")
|
||||
client, err := api.NewAuthorizedClient(srv.URL, tokenCache.GetToken, http.DefaultClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := client.ExchangeClusterIdentityTokenWithResponse(context.Background(),
|
||||
api.ExchangeTokenRequest{
|
||||
RefreshToken: "refresh-token",
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, idToken, resp.JSON200.IdToken)
|
||||
}
|
5
pkg/zero/cluster/generate.go
Normal file
5
pkg/zero/cluster/generate.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package cluster
|
||||
|
||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=models.yaml openapi.yaml
|
||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.yaml openapi.yaml
|
||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=client.yaml openapi.yaml
|
99
pkg/zero/cluster/models.gen.go
Normal file
99
pkg/zero/cluster/models.gen.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Package cluster provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/deepmap/oapi-codegen version v1.16.2 DO NOT EDIT.
|
||||
package cluster
|
||||
|
||||
const (
|
||||
BearerAuthScopes = "bearerAuth.Scopes"
|
||||
)
|
||||
|
||||
// Defines values for BundleStatusFailureSource.
|
||||
const (
|
||||
DatabrokerError BundleStatusFailureSource = "databroker_error"
|
||||
DownloadError BundleStatusFailureSource = "download_error"
|
||||
InvalidBundle BundleStatusFailureSource = "invalid_bundle"
|
||||
IoError BundleStatusFailureSource = "io_error"
|
||||
UnknownError BundleStatusFailureSource = "unknown_error"
|
||||
)
|
||||
|
||||
// BootstrapConfig defines model for BootstrapConfig.
|
||||
type BootstrapConfig struct {
|
||||
// DatabrokerStorageConnection databroker storage connection string
|
||||
DatabrokerStorageConnection *string `json:"databrokerStorageConnection,omitempty"`
|
||||
}
|
||||
|
||||
// Bundle defines model for Bundle.
|
||||
type Bundle struct {
|
||||
// Id bundle id
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// BundleStatus defines model for BundleStatus.
|
||||
type BundleStatus struct {
|
||||
Failure *BundleStatusFailure `json:"failure,omitempty"`
|
||||
Success *BundleStatusSuccess `json:"success,omitempty"`
|
||||
}
|
||||
|
||||
// BundleStatusFailure defines model for BundleStatusFailure.
|
||||
type BundleStatusFailure struct {
|
||||
Message string `json:"message"`
|
||||
|
||||
// Source source of the failure
|
||||
Source BundleStatusFailureSource `json:"source"`
|
||||
}
|
||||
|
||||
// BundleStatusFailureSource source of the failure
|
||||
type BundleStatusFailureSource string
|
||||
|
||||
// BundleStatusSuccess defines model for BundleStatusSuccess.
|
||||
type BundleStatusSuccess struct {
|
||||
// Metadata bundle metadata
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
// DownloadBundleResponse defines model for DownloadBundleResponse.
|
||||
type DownloadBundleResponse struct {
|
||||
// CaptureMetadataHeaders bundle metadata that need be picked up by the client from the download URL
|
||||
CaptureMetadataHeaders []string `json:"captureMetadataHeaders"`
|
||||
ExpiresInSeconds string `json:"expiresInSeconds"`
|
||||
|
||||
// Url download URL
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
// ErrorResponse defines model for ErrorResponse.
|
||||
type ErrorResponse struct {
|
||||
// Error Error message
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// ExchangeTokenRequest defines model for ExchangeTokenRequest.
|
||||
type ExchangeTokenRequest struct {
|
||||
// RefreshToken cluster identity token
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
// ExchangeTokenResponse defines model for ExchangeTokenResponse.
|
||||
type ExchangeTokenResponse struct {
|
||||
ExpiresInSeconds string `json:"expiresInSeconds"`
|
||||
|
||||
// IdToken ID token
|
||||
IdToken string `json:"idToken"`
|
||||
}
|
||||
|
||||
// GetBootstrapConfigResponse defines model for GetBootstrapConfigResponse.
|
||||
type GetBootstrapConfigResponse = BootstrapConfig
|
||||
|
||||
// GetBundlesResponse defines model for GetBundlesResponse.
|
||||
type GetBundlesResponse struct {
|
||||
Bundles []Bundle `json:"bundles"`
|
||||
}
|
||||
|
||||
// BundleId defines model for bundleId.
|
||||
type BundleId = string
|
||||
|
||||
// ReportClusterResourceBundleStatusJSONRequestBody defines body for ReportClusterResourceBundleStatus for application/json ContentType.
|
||||
type ReportClusterResourceBundleStatusJSONRequestBody = BundleStatus
|
||||
|
||||
// ExchangeClusterIdentityTokenJSONRequestBody defines body for ExchangeClusterIdentityToken for application/json ContentType.
|
||||
type ExchangeClusterIdentityTokenJSONRequestBody = ExchangeTokenRequest
|
4
pkg/zero/cluster/models.yaml
Normal file
4
pkg/zero/cluster/models.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
package: cluster
|
||||
generate:
|
||||
models: true
|
||||
output: models.gen.go
|
269
pkg/zero/cluster/openapi.yaml
Normal file
269
pkg/zero/cluster/openapi.yaml
Normal file
|
@ -0,0 +1,269 @@
|
|||
openapi: 3.0.0
|
||||
|
||||
info:
|
||||
title: "Pomerium Zero SDK API"
|
||||
version: 0.1.0
|
||||
|
||||
servers:
|
||||
- url: /cluster/v1
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
|
||||
paths:
|
||||
/bootstrap:
|
||||
get:
|
||||
description: Get cluster bootstrap configuration
|
||||
operationId: getClusterBootstrapConfig
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GetBootstrapConfigResponse"
|
||||
"400":
|
||||
description: Bad Request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/bundles:
|
||||
get:
|
||||
description: Get all cluster resource bundles
|
||||
operationId: getClusterResourceBundles
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GetBundlesResponse"
|
||||
"400":
|
||||
description: Bad Request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/bundles/{bundleId}/download:
|
||||
get:
|
||||
description: Download cluster resource bundle
|
||||
operationId: downloadClusterResourceBundle
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/bundleId"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DownloadBundleResponse"
|
||||
"400":
|
||||
description: Bad Request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"404":
|
||||
description: Not Found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/bundles/{bundleId}/status:
|
||||
post:
|
||||
description: Report cluster resource bundle status
|
||||
operationId: reportClusterResourceBundleStatus
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/bundleId"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BundleStatus"
|
||||
responses:
|
||||
"204":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad Request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
/exchangeToken:
|
||||
post:
|
||||
description: Exchange cluster identity token for bearer token
|
||||
operationId: exchangeClusterIdentityToken
|
||||
tags: [token]
|
||||
security: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ExchangeTokenRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ExchangeTokenResponse"
|
||||
"400":
|
||||
description: Bad Request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
|
||||
components:
|
||||
parameters:
|
||||
bundleId:
|
||||
name: bundleId
|
||||
in: path
|
||||
description: bundle id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
|
||||
schemas:
|
||||
BootstrapConfig:
|
||||
type: object
|
||||
properties:
|
||||
databrokerStorageConnection:
|
||||
type: string
|
||||
description: databroker storage connection string
|
||||
|
||||
Bundle:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: bundle id
|
||||
required:
|
||||
- id
|
||||
BundleStatus:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
$ref: "#/components/schemas/BundleStatusSuccess"
|
||||
failure:
|
||||
$ref: "#/components/schemas/BundleStatusFailure"
|
||||
BundleStatusSuccess:
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
description: bundle metadata
|
||||
additionalProperties:
|
||||
type: string
|
||||
required:
|
||||
- metadata
|
||||
BundleStatusFailure:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
description: source of the failure
|
||||
enum:
|
||||
- download_error
|
||||
- io_error
|
||||
- invalid_bundle
|
||||
- databroker_error
|
||||
- unknown_error
|
||||
required:
|
||||
- source
|
||||
- message
|
||||
|
||||
DownloadBundleResponse:
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: download URL
|
||||
expiresInSeconds:
|
||||
type: string
|
||||
format: int64
|
||||
captureMetadataHeaders:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: bundle metadata that need be picked up by the client from the download URL
|
||||
required:
|
||||
- url
|
||||
- expiresInSeconds
|
||||
- captureMetadataHeaders
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
description: Error message
|
||||
required:
|
||||
- error
|
||||
ExchangeTokenRequest:
|
||||
type: object
|
||||
properties:
|
||||
refreshToken:
|
||||
type: string
|
||||
description: cluster identity token
|
||||
required:
|
||||
- refreshToken
|
||||
ExchangeTokenResponse:
|
||||
type: object
|
||||
properties:
|
||||
idToken:
|
||||
type: string
|
||||
description: ID token
|
||||
expiresInSeconds:
|
||||
type: string
|
||||
format: int64
|
||||
required:
|
||||
- idToken
|
||||
- expiresInSeconds
|
||||
GetBootstrapConfigResponse:
|
||||
$ref: "#/components/schemas/BootstrapConfig"
|
||||
GetBundlesResponse:
|
||||
type: object
|
||||
properties:
|
||||
bundles:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Bundle"
|
||||
required:
|
||||
- bundles
|
677
pkg/zero/cluster/server.gen.go
Normal file
677
pkg/zero/cluster/server.gen.go
Normal file
|
@ -0,0 +1,677 @@
|
|||
// Package cluster provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/deepmap/oapi-codegen version v1.16.2 DO NOT EDIT.
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/oapi-codegen/runtime"
|
||||
strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp"
|
||||
)
|
||||
|
||||
// ServerInterface represents all server handlers.
|
||||
type ServerInterface interface {
|
||||
|
||||
// (GET /bootstrap)
|
||||
GetClusterBootstrapConfig(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// (GET /bundles)
|
||||
GetClusterResourceBundles(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// (GET /bundles/{bundleId}/download)
|
||||
DownloadClusterResourceBundle(w http.ResponseWriter, r *http.Request, bundleId BundleId)
|
||||
|
||||
// (POST /bundles/{bundleId}/status)
|
||||
ReportClusterResourceBundleStatus(w http.ResponseWriter, r *http.Request, bundleId BundleId)
|
||||
|
||||
// (POST /exchangeToken)
|
||||
ExchangeClusterIdentityToken(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint.
|
||||
|
||||
type Unimplemented struct{}
|
||||
|
||||
// (GET /bootstrap)
|
||||
func (_ Unimplemented) GetClusterBootstrapConfig(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// (GET /bundles)
|
||||
func (_ Unimplemented) GetClusterResourceBundles(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// (GET /bundles/{bundleId}/download)
|
||||
func (_ Unimplemented) DownloadClusterResourceBundle(w http.ResponseWriter, r *http.Request, bundleId BundleId) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// (POST /bundles/{bundleId}/status)
|
||||
func (_ Unimplemented) ReportClusterResourceBundleStatus(w http.ResponseWriter, r *http.Request, bundleId BundleId) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// (POST /exchangeToken)
|
||||
func (_ Unimplemented) ExchangeClusterIdentityToken(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
type ServerInterfaceWrapper struct {
|
||||
Handler ServerInterface
|
||||
HandlerMiddlewares []MiddlewareFunc
|
||||
ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
|
||||
}
|
||||
|
||||
type MiddlewareFunc func(http.Handler) http.Handler
|
||||
|
||||
// GetClusterBootstrapConfig operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetClusterBootstrapConfig(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.GetClusterBootstrapConfig(w, r)
|
||||
}))
|
||||
|
||||
for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- {
|
||||
handler = siw.HandlerMiddlewares[i](handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// GetClusterResourceBundles operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetClusterResourceBundles(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.GetClusterResourceBundles(w, r)
|
||||
}))
|
||||
|
||||
for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- {
|
||||
handler = siw.HandlerMiddlewares[i](handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// DownloadClusterResourceBundle operation middleware
|
||||
func (siw *ServerInterfaceWrapper) DownloadClusterResourceBundle(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "bundleId" -------------
|
||||
var bundleId BundleId
|
||||
|
||||
err = runtime.BindStyledParameterWithLocation("simple", false, "bundleId", runtime.ParamLocationPath, chi.URLParam(r, "bundleId"), &bundleId)
|
||||
if err != nil {
|
||||
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bundleId", Err: err})
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.DownloadClusterResourceBundle(w, r, bundleId)
|
||||
}))
|
||||
|
||||
for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- {
|
||||
handler = siw.HandlerMiddlewares[i](handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// ReportClusterResourceBundleStatus operation middleware
|
||||
func (siw *ServerInterfaceWrapper) ReportClusterResourceBundleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "bundleId" -------------
|
||||
var bundleId BundleId
|
||||
|
||||
err = runtime.BindStyledParameterWithLocation("simple", false, "bundleId", runtime.ParamLocationPath, chi.URLParam(r, "bundleId"), &bundleId)
|
||||
if err != nil {
|
||||
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "bundleId", Err: err})
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, BearerAuthScopes, []string{})
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.ReportClusterResourceBundleStatus(w, r, bundleId)
|
||||
}))
|
||||
|
||||
for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- {
|
||||
handler = siw.HandlerMiddlewares[i](handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// ExchangeClusterIdentityToken operation middleware
|
||||
func (siw *ServerInterfaceWrapper) ExchangeClusterIdentityToken(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
siw.Handler.ExchangeClusterIdentityToken(w, r)
|
||||
}))
|
||||
|
||||
for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- {
|
||||
handler = siw.HandlerMiddlewares[i](handler)
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
type UnescapedCookieParamError struct {
|
||||
ParamName string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *UnescapedCookieParamError) Error() string {
|
||||
return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
|
||||
}
|
||||
|
||||
func (e *UnescapedCookieParamError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
type UnmarshalingParamError struct {
|
||||
ParamName string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *UnmarshalingParamError) Error() string {
|
||||
return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *UnmarshalingParamError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
type RequiredParamError struct {
|
||||
ParamName string
|
||||
}
|
||||
|
||||
func (e *RequiredParamError) Error() string {
|
||||
return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
|
||||
}
|
||||
|
||||
type RequiredHeaderError struct {
|
||||
ParamName string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *RequiredHeaderError) Error() string {
|
||||
return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
|
||||
}
|
||||
|
||||
func (e *RequiredHeaderError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
type InvalidParamFormatError struct {
|
||||
ParamName string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *InvalidParamFormatError) Error() string {
|
||||
return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *InvalidParamFormatError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
type TooManyValuesForParamError struct {
|
||||
ParamName string
|
||||
Count int
|
||||
}
|
||||
|
||||
func (e *TooManyValuesForParamError) Error() string {
|
||||
return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
|
||||
}
|
||||
|
||||
// Handler creates http.Handler with routing matching OpenAPI spec.
|
||||
func Handler(si ServerInterface) http.Handler {
|
||||
return HandlerWithOptions(si, ChiServerOptions{})
|
||||
}
|
||||
|
||||
type ChiServerOptions struct {
|
||||
BaseURL string
|
||||
BaseRouter chi.Router
|
||||
Middlewares []MiddlewareFunc
|
||||
ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
|
||||
}
|
||||
|
||||
// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
|
||||
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
|
||||
return HandlerWithOptions(si, ChiServerOptions{
|
||||
BaseRouter: r,
|
||||
})
|
||||
}
|
||||
|
||||
func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler {
|
||||
return HandlerWithOptions(si, ChiServerOptions{
|
||||
BaseURL: baseURL,
|
||||
BaseRouter: r,
|
||||
})
|
||||
}
|
||||
|
||||
// HandlerWithOptions creates http.Handler with additional options
|
||||
func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler {
|
||||
r := options.BaseRouter
|
||||
|
||||
if r == nil {
|
||||
r = chi.NewRouter()
|
||||
}
|
||||
if options.ErrorHandlerFunc == nil {
|
||||
options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
wrapper := ServerInterfaceWrapper{
|
||||
Handler: si,
|
||||
HandlerMiddlewares: options.Middlewares,
|
||||
ErrorHandlerFunc: options.ErrorHandlerFunc,
|
||||
}
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get(options.BaseURL+"/bootstrap", wrapper.GetClusterBootstrapConfig)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get(options.BaseURL+"/bundles", wrapper.GetClusterResourceBundles)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get(options.BaseURL+"/bundles/{bundleId}/download", wrapper.DownloadClusterResourceBundle)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Post(options.BaseURL+"/bundles/{bundleId}/status", wrapper.ReportClusterResourceBundleStatus)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Post(options.BaseURL+"/exchangeToken", wrapper.ExchangeClusterIdentityToken)
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type GetClusterBootstrapConfigRequestObject struct {
|
||||
}
|
||||
|
||||
type GetClusterBootstrapConfigResponseObject interface {
|
||||
VisitGetClusterBootstrapConfigResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type GetClusterBootstrapConfig200JSONResponse GetBootstrapConfigResponse
|
||||
|
||||
func (response GetClusterBootstrapConfig200JSONResponse) VisitGetClusterBootstrapConfigResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetClusterBootstrapConfig400JSONResponse ErrorResponse
|
||||
|
||||
func (response GetClusterBootstrapConfig400JSONResponse) VisitGetClusterBootstrapConfigResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetClusterBootstrapConfig500JSONResponse ErrorResponse
|
||||
|
||||
func (response GetClusterBootstrapConfig500JSONResponse) VisitGetClusterBootstrapConfigResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetClusterResourceBundlesRequestObject struct {
|
||||
}
|
||||
|
||||
type GetClusterResourceBundlesResponseObject interface {
|
||||
VisitGetClusterResourceBundlesResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type GetClusterResourceBundles200JSONResponse GetBundlesResponse
|
||||
|
||||
func (response GetClusterResourceBundles200JSONResponse) VisitGetClusterResourceBundlesResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetClusterResourceBundles400JSONResponse ErrorResponse
|
||||
|
||||
func (response GetClusterResourceBundles400JSONResponse) VisitGetClusterResourceBundlesResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type GetClusterResourceBundles500JSONResponse ErrorResponse
|
||||
|
||||
func (response GetClusterResourceBundles500JSONResponse) VisitGetClusterResourceBundlesResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundleRequestObject struct {
|
||||
BundleId BundleId `json:"bundleId"`
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundleResponseObject interface {
|
||||
VisitDownloadClusterResourceBundleResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundle200JSONResponse DownloadBundleResponse
|
||||
|
||||
func (response DownloadClusterResourceBundle200JSONResponse) VisitDownloadClusterResourceBundleResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundle400JSONResponse ErrorResponse
|
||||
|
||||
func (response DownloadClusterResourceBundle400JSONResponse) VisitDownloadClusterResourceBundleResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundle404JSONResponse ErrorResponse
|
||||
|
||||
func (response DownloadClusterResourceBundle404JSONResponse) VisitDownloadClusterResourceBundleResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(404)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type DownloadClusterResourceBundle500JSONResponse ErrorResponse
|
||||
|
||||
func (response DownloadClusterResourceBundle500JSONResponse) VisitDownloadClusterResourceBundleResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type ReportClusterResourceBundleStatusRequestObject struct {
|
||||
BundleId BundleId `json:"bundleId"`
|
||||
Body *ReportClusterResourceBundleStatusJSONRequestBody
|
||||
}
|
||||
|
||||
type ReportClusterResourceBundleStatusResponseObject interface {
|
||||
VisitReportClusterResourceBundleStatusResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type ReportClusterResourceBundleStatus204Response struct {
|
||||
}
|
||||
|
||||
func (response ReportClusterResourceBundleStatus204Response) VisitReportClusterResourceBundleStatusResponse(w http.ResponseWriter) error {
|
||||
w.WriteHeader(204)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReportClusterResourceBundleStatus400JSONResponse ErrorResponse
|
||||
|
||||
func (response ReportClusterResourceBundleStatus400JSONResponse) VisitReportClusterResourceBundleStatusResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type ReportClusterResourceBundleStatus500JSONResponse ErrorResponse
|
||||
|
||||
func (response ReportClusterResourceBundleStatus500JSONResponse) VisitReportClusterResourceBundleStatusResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type ExchangeClusterIdentityTokenRequestObject struct {
|
||||
Body *ExchangeClusterIdentityTokenJSONRequestBody
|
||||
}
|
||||
|
||||
type ExchangeClusterIdentityTokenResponseObject interface {
|
||||
VisitExchangeClusterIdentityTokenResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type ExchangeClusterIdentityToken200JSONResponse ExchangeTokenResponse
|
||||
|
||||
func (response ExchangeClusterIdentityToken200JSONResponse) VisitExchangeClusterIdentityTokenResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type ExchangeClusterIdentityToken400JSONResponse ErrorResponse
|
||||
|
||||
func (response ExchangeClusterIdentityToken400JSONResponse) VisitExchangeClusterIdentityTokenResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type ExchangeClusterIdentityToken500JSONResponse ErrorResponse
|
||||
|
||||
func (response ExchangeClusterIdentityToken500JSONResponse) VisitExchangeClusterIdentityTokenResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// StrictServerInterface represents all server handlers.
|
||||
type StrictServerInterface interface {
|
||||
|
||||
// (GET /bootstrap)
|
||||
GetClusterBootstrapConfig(ctx context.Context, request GetClusterBootstrapConfigRequestObject) (GetClusterBootstrapConfigResponseObject, error)
|
||||
|
||||
// (GET /bundles)
|
||||
GetClusterResourceBundles(ctx context.Context, request GetClusterResourceBundlesRequestObject) (GetClusterResourceBundlesResponseObject, error)
|
||||
|
||||
// (GET /bundles/{bundleId}/download)
|
||||
DownloadClusterResourceBundle(ctx context.Context, request DownloadClusterResourceBundleRequestObject) (DownloadClusterResourceBundleResponseObject, error)
|
||||
|
||||
// (POST /bundles/{bundleId}/status)
|
||||
ReportClusterResourceBundleStatus(ctx context.Context, request ReportClusterResourceBundleStatusRequestObject) (ReportClusterResourceBundleStatusResponseObject, error)
|
||||
|
||||
// (POST /exchangeToken)
|
||||
ExchangeClusterIdentityToken(ctx context.Context, request ExchangeClusterIdentityTokenRequestObject) (ExchangeClusterIdentityTokenResponseObject, error)
|
||||
}
|
||||
|
||||
type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc
|
||||
type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc
|
||||
|
||||
type StrictHTTPServerOptions struct {
|
||||
RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
|
||||
ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
|
||||
}
|
||||
|
||||
func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
|
||||
return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictHTTPServerOptions{
|
||||
RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
},
|
||||
ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface {
|
||||
return &strictHandler{ssi: ssi, middlewares: middlewares, options: options}
|
||||
}
|
||||
|
||||
type strictHandler struct {
|
||||
ssi StrictServerInterface
|
||||
middlewares []StrictMiddlewareFunc
|
||||
options StrictHTTPServerOptions
|
||||
}
|
||||
|
||||
// GetClusterBootstrapConfig operation middleware
|
||||
func (sh *strictHandler) GetClusterBootstrapConfig(w http.ResponseWriter, r *http.Request) {
|
||||
var request GetClusterBootstrapConfigRequestObject
|
||||
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.GetClusterBootstrapConfig(ctx, request.(GetClusterBootstrapConfigRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "GetClusterBootstrapConfig")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(GetClusterBootstrapConfigResponseObject); ok {
|
||||
if err := validResponse.VisitGetClusterBootstrapConfigResponse(w); err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
}
|
||||
} else if response != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
||||
|
||||
// GetClusterResourceBundles operation middleware
|
||||
func (sh *strictHandler) GetClusterResourceBundles(w http.ResponseWriter, r *http.Request) {
|
||||
var request GetClusterResourceBundlesRequestObject
|
||||
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.GetClusterResourceBundles(ctx, request.(GetClusterResourceBundlesRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "GetClusterResourceBundles")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(GetClusterResourceBundlesResponseObject); ok {
|
||||
if err := validResponse.VisitGetClusterResourceBundlesResponse(w); err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
}
|
||||
} else if response != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadClusterResourceBundle operation middleware
|
||||
func (sh *strictHandler) DownloadClusterResourceBundle(w http.ResponseWriter, r *http.Request, bundleId BundleId) {
|
||||
var request DownloadClusterResourceBundleRequestObject
|
||||
|
||||
request.BundleId = bundleId
|
||||
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.DownloadClusterResourceBundle(ctx, request.(DownloadClusterResourceBundleRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "DownloadClusterResourceBundle")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(DownloadClusterResourceBundleResponseObject); ok {
|
||||
if err := validResponse.VisitDownloadClusterResourceBundleResponse(w); err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
}
|
||||
} else if response != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
||||
|
||||
// ReportClusterResourceBundleStatus operation middleware
|
||||
func (sh *strictHandler) ReportClusterResourceBundleStatus(w http.ResponseWriter, r *http.Request, bundleId BundleId) {
|
||||
var request ReportClusterResourceBundleStatusRequestObject
|
||||
|
||||
request.BundleId = bundleId
|
||||
|
||||
var body ReportClusterResourceBundleStatusJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
|
||||
return
|
||||
}
|
||||
request.Body = &body
|
||||
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.ReportClusterResourceBundleStatus(ctx, request.(ReportClusterResourceBundleStatusRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "ReportClusterResourceBundleStatus")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(ReportClusterResourceBundleStatusResponseObject); ok {
|
||||
if err := validResponse.VisitReportClusterResourceBundleStatusResponse(w); err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
}
|
||||
} else if response != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
||||
|
||||
// ExchangeClusterIdentityToken operation middleware
|
||||
func (sh *strictHandler) ExchangeClusterIdentityToken(w http.ResponseWriter, r *http.Request) {
|
||||
var request ExchangeClusterIdentityTokenRequestObject
|
||||
|
||||
var body ExchangeClusterIdentityTokenJSONRequestBody
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
|
||||
return
|
||||
}
|
||||
request.Body = &body
|
||||
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.ExchangeClusterIdentityToken(ctx, request.(ExchangeClusterIdentityTokenRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "ExchangeClusterIdentityToken")
|
||||
}
|
||||
|
||||
response, err := handler(r.Context(), w, r, request)
|
||||
|
||||
if err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
} else if validResponse, ok := response.(ExchangeClusterIdentityTokenResponseObject); ok {
|
||||
if err := validResponse.VisitExchangeClusterIdentityTokenResponse(w); err != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, err)
|
||||
}
|
||||
} else if response != nil {
|
||||
sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
|
||||
}
|
||||
}
|
8
pkg/zero/cluster/server.yaml
Normal file
8
pkg/zero/cluster/server.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
package: cluster
|
||||
generate:
|
||||
chi-server: true
|
||||
strict-server: true
|
||||
models: false
|
||||
compatibility:
|
||||
apply-chi-middleware-first-to-last: true
|
||||
output: server.gen.go
|
40
pkg/zero/cluster/token_fetcher.go
Normal file
40
pkg/zero/cluster/token_fetcher.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
"github.com/pomerium/pomerium/pkg/zero/token"
|
||||
)
|
||||
|
||||
// NewTokenFetcher creates a new authorization token fetcher
|
||||
func NewTokenFetcher(endpoint string, opts ...ClientOption) (token.Fetcher, error) {
|
||||
client, err := NewClientWithResponses(endpoint, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating client: %w", err)
|
||||
}
|
||||
|
||||
return func(ctx context.Context, refreshToken string) (*token.Token, error) {
|
||||
now := time.Now()
|
||||
|
||||
resp, err := apierror.CheckResponse[ExchangeTokenResponse](client.ExchangeClusterIdentityTokenWithResponse(ctx, ExchangeTokenRequest{
|
||||
RefreshToken: refreshToken,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error exchanging token: %w", err)
|
||||
}
|
||||
|
||||
expiresSeconds, err := strconv.ParseInt(resp.ExpiresInSeconds, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing expires in: %w", err)
|
||||
}
|
||||
|
||||
return &token.Token{
|
||||
Bearer: resp.IdToken,
|
||||
Expires: now.Add(time.Duration(expiresSeconds) * time.Second),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
55
pkg/zero/cluster/urlcache.go
Normal file
55
pkg/zero/cluster/urlcache.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// URLCache is a cache of URLs to download bundles from.
|
||||
type URLCache struct {
|
||||
mx sync.RWMutex
|
||||
cache map[string]DownloadCacheEntry
|
||||
}
|
||||
|
||||
// DownloadCacheEntry is a cache entry for a URL to download a bundle from.
|
||||
type DownloadCacheEntry struct {
|
||||
// URL is the URL to download the bundle from.
|
||||
URL url.URL
|
||||
// ExpiresAt is the time at which the URL expires.
|
||||
ExpiresAt time.Time
|
||||
// CaptureHeaders is a list of headers to capture from the response.
|
||||
CaptureHeaders []string
|
||||
}
|
||||
|
||||
// NewURLCache creates a new URL cache.
|
||||
func NewURLCache() *URLCache {
|
||||
return &URLCache{
|
||||
cache: make(map[string]DownloadCacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// Get gets the cache entry for the given key, if it exists and has not expired.
|
||||
func (c *URLCache) Get(key string, minTTL time.Duration) (*DownloadCacheEntry, bool) {
|
||||
c.mx.RLock()
|
||||
defer c.mx.RUnlock()
|
||||
|
||||
entry, ok := c.cache[key]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if time.Until(entry.ExpiresAt) < minTTL {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &entry, true
|
||||
}
|
||||
|
||||
// Set sets the cache entry for the given key.
|
||||
func (c *URLCache) Set(key string, entry DownloadCacheEntry) {
|
||||
c.mx.Lock()
|
||||
defer c.mx.Unlock()
|
||||
|
||||
c.cache[key] = entry
|
||||
}
|
87
pkg/zero/config.go
Normal file
87
pkg/zero/config.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package zero
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Option is a functional option for the SDK
|
||||
type Option func(*config)
|
||||
|
||||
type config struct {
|
||||
clusterAPIEndpoint string
|
||||
connectAPIEndpoint string
|
||||
apiToken string
|
||||
httpClient *http.Client
|
||||
downloadURLCacheTTL time.Duration
|
||||
}
|
||||
|
||||
// WithClusterAPIEndpoint sets the cluster API endpoint
|
||||
func WithClusterAPIEndpoint(endpoint string) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.clusterAPIEndpoint = endpoint
|
||||
}
|
||||
}
|
||||
|
||||
// WithConnectAPIEndpoint sets the connect API endpoint
|
||||
func WithConnectAPIEndpoint(endpoint string) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.connectAPIEndpoint = endpoint
|
||||
}
|
||||
}
|
||||
|
||||
// WithAPIToken sets the API token
|
||||
func WithAPIToken(token string) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.apiToken = token
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient sets the HTTP client
|
||||
func WithHTTPClient(client *http.Client) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.httpClient = client
|
||||
}
|
||||
}
|
||||
|
||||
// WithDownloadURLCacheTTL sets the minimum TTL for download URL cache entries
|
||||
func WithDownloadURLCacheTTL(ttl time.Duration) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.downloadURLCacheTTL = ttl
|
||||
}
|
||||
}
|
||||
|
||||
func newConfig(opts ...Option) (*config, error) {
|
||||
cfg := new(config)
|
||||
for _, opt := range []Option{
|
||||
WithHTTPClient(http.DefaultClient),
|
||||
WithDownloadURLCacheTTL(15 * time.Minute),
|
||||
} {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
if err := cfg.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (c *config) validate() error {
|
||||
if c.clusterAPIEndpoint == "" {
|
||||
return fmt.Errorf("cluster API endpoint is required")
|
||||
}
|
||||
if c.connectAPIEndpoint == "" {
|
||||
return fmt.Errorf("connect API endpoint is required")
|
||||
}
|
||||
if c.apiToken == "" {
|
||||
return fmt.Errorf("API token is required")
|
||||
}
|
||||
if c.httpClient == nil {
|
||||
return fmt.Errorf("HTTP client is required")
|
||||
}
|
||||
return nil
|
||||
}
|
57
pkg/zero/connect-mux/config.go
Normal file
57
pkg/zero/connect-mux/config.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package mux
|
||||
|
||||
import "context"
|
||||
|
||||
type config struct {
|
||||
onConnected func(ctx context.Context)
|
||||
onDisconnected func(ctx context.Context)
|
||||
onBundleUpdated func(ctx context.Context, key string)
|
||||
onBootstrapConfigUpdated func(ctx context.Context)
|
||||
}
|
||||
|
||||
// WatchOption allows to specify callbacks for various events
|
||||
type WatchOption func(*config)
|
||||
|
||||
// WithOnConnected sets the callback for when the connection is established
|
||||
func WithOnConnected(onConnected func(context.Context)) WatchOption {
|
||||
return func(cfg *config) {
|
||||
cfg.onConnected = onConnected
|
||||
}
|
||||
}
|
||||
|
||||
// WithOnDisconnected sets the callback for when the connection is lost
|
||||
func WithOnDisconnected(onDisconnected func(context.Context)) WatchOption {
|
||||
return func(cfg *config) {
|
||||
cfg.onDisconnected = onDisconnected
|
||||
}
|
||||
}
|
||||
|
||||
// WithOnBundleUpdated sets the callback for when the bundle is updated
|
||||
func WithOnBundleUpdated(onBundleUpdated func(ctx context.Context, key string)) WatchOption {
|
||||
return func(cfg *config) {
|
||||
cfg.onBundleUpdated = onBundleUpdated
|
||||
}
|
||||
}
|
||||
|
||||
// WithOnBootstrapConfigUpdated sets the callback for when the bootstrap config is updated
|
||||
func WithOnBootstrapConfigUpdated(onBootstrapConfigUpdated func(context.Context)) WatchOption {
|
||||
return func(cfg *config) {
|
||||
cfg.onBootstrapConfigUpdated = onBootstrapConfigUpdated
|
||||
}
|
||||
}
|
||||
|
||||
func newConfig(opts ...WatchOption) *config {
|
||||
cfg := &config{}
|
||||
for _, opt := range []WatchOption{
|
||||
WithOnConnected(func(_ context.Context) {}),
|
||||
WithOnDisconnected(func(_ context.Context) {}),
|
||||
WithOnBundleUpdated(func(_ context.Context, key string) {}),
|
||||
WithOnBootstrapConfigUpdated(func(_ context.Context) {}),
|
||||
} {
|
||||
opt(cfg)
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
109
pkg/zero/connect-mux/messages.go
Normal file
109
pkg/zero/connect-mux/messages.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
package mux
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
"github.com/pomerium/pomerium/pkg/zero/connect"
|
||||
)
|
||||
|
||||
// Watch watches for changes to the config until either context is canceled,
|
||||
// or an error occurs while muxing
|
||||
func (svc *Mux) Watch(ctx context.Context, opts ...WatchOption) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-svc.ready:
|
||||
}
|
||||
|
||||
cfg := newConfig(opts...)
|
||||
|
||||
connected := svc.connected.Load()
|
||||
if connected {
|
||||
cfg.onConnected(ctx)
|
||||
} else {
|
||||
cfg.onDisconnected(ctx)
|
||||
}
|
||||
|
||||
return svc.mux.Receive(ctx, func(ctx context.Context, msg message) error {
|
||||
return dispatch(ctx, cfg, msg)
|
||||
})
|
||||
}
|
||||
|
||||
func dispatch(ctx context.Context, cfg *config, msg message) error {
|
||||
switch {
|
||||
case msg.stateChange != nil:
|
||||
switch *msg.stateChange {
|
||||
case connected:
|
||||
cfg.onConnected(ctx)
|
||||
case disconnected:
|
||||
cfg.onDisconnected(ctx)
|
||||
default:
|
||||
return fmt.Errorf("unknown state change")
|
||||
}
|
||||
case msg.Message != nil:
|
||||
switch msg.Message.Message.(type) {
|
||||
case *connect.Message_ConfigUpdated:
|
||||
cfg.onBundleUpdated(ctx, "config")
|
||||
case *connect.Message_BootstrapConfigUpdated:
|
||||
cfg.onBootstrapConfigUpdated(ctx)
|
||||
default:
|
||||
return fmt.Errorf("unknown message type")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown message payload")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type message struct {
|
||||
*stateChange
|
||||
*connect.Message
|
||||
}
|
||||
|
||||
type stateChange string
|
||||
|
||||
const (
|
||||
connected stateChange = "connected"
|
||||
disconnected stateChange = "disconnected"
|
||||
)
|
||||
|
||||
// Publish publishes a message to the fanout
|
||||
// we treat errors returned from the fanout as terminal,
|
||||
// as they are generally non recoverable
|
||||
func (svc *Mux) publish(ctx context.Context, msg message) error {
|
||||
err := svc.mux.Publish(ctx, msg)
|
||||
if err != nil {
|
||||
return apierror.NewTerminalError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Mux) onConnected(ctx context.Context) error {
|
||||
s := connected
|
||||
svc.connected.Store(true)
|
||||
err := svc.publish(ctx, message{stateChange: &s})
|
||||
if err != nil {
|
||||
return fmt.Errorf("onConnected: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Mux) onDisconnected(ctx context.Context) error {
|
||||
s := disconnected
|
||||
svc.connected.Store(false)
|
||||
err := svc.publish(ctx, message{stateChange: &s})
|
||||
if err != nil {
|
||||
return fmt.Errorf("onDisconnected: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Mux) onMessage(ctx context.Context, msg *connect.Message) error {
|
||||
err := svc.publish(ctx, message{Message: msg})
|
||||
if err != nil {
|
||||
return fmt.Errorf("onMessage: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
108
pkg/zero/connect-mux/service.go
Normal file
108
pkg/zero/connect-mux/service.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Package mux provides the way to listen for updates from the cloud
|
||||
package mux
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/fanout"
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
"github.com/pomerium/pomerium/pkg/zero/connect"
|
||||
)
|
||||
|
||||
// Mux is the service that listens for updates from the cloud
|
||||
type Mux struct {
|
||||
client connect.ConnectClient
|
||||
mux *fanout.FanOut[message]
|
||||
|
||||
ready chan struct{}
|
||||
|
||||
connected atomic.Bool
|
||||
}
|
||||
|
||||
// New creates a new mux service that listens for updates from the cloud
|
||||
func New(client connect.ConnectClient) *Mux {
|
||||
svc := &Mux{
|
||||
client: client,
|
||||
ready: make(chan struct{}),
|
||||
}
|
||||
return svc
|
||||
}
|
||||
|
||||
// Run starts the service
|
||||
func (svc *Mux) Run(ctx context.Context, opts ...fanout.Option) error {
|
||||
ctx, cancel := context.WithCancelCause(ctx)
|
||||
defer func() { cancel(ctx.Err()) }()
|
||||
|
||||
svc.mux = fanout.Start[message](ctx, opts...)
|
||||
close(svc.ready)
|
||||
|
||||
err := svc.run(ctx)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Mux) run(ctx context.Context) error {
|
||||
bo := backoff.NewExponentialBackOff()
|
||||
bo.MaxElapsedTime = 0
|
||||
|
||||
ticker := time.NewTicker(time.Microsecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
}
|
||||
|
||||
err := svc.subscribeAndDispatch(ctx, bo.Reset)
|
||||
if err != nil {
|
||||
ticker.Reset(bo.NextBackOff())
|
||||
}
|
||||
|
||||
if apierror.IsTerminalError(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *Mux) subscribeAndDispatch(ctx context.Context, onConnected func()) (err error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
stream, err := svc.client.Subscribe(ctx, &connect.SubscribeRequest{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("subscribe: %w", err)
|
||||
}
|
||||
onConnected()
|
||||
|
||||
if err = svc.onConnected(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = multierror.Append(err, svc.onDisconnected(ctx)).ErrorOrNil()
|
||||
}()
|
||||
|
||||
log.Ctx(ctx).Info().Msg("subscribed to connect service")
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
log.Ctx(ctx).Info().Interface("msg", msg).Err(err).Msg("receive")
|
||||
if err != nil {
|
||||
return fmt.Errorf("receive: %w", err)
|
||||
}
|
||||
err = svc.onMessage(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
10
pkg/zero/connect/buf.gen.yaml
Normal file
10
pkg/zero/connect/buf.gen.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
version: v1
|
||||
plugins:
|
||||
- name: go
|
||||
out: .
|
||||
opt: paths=source_relative
|
||||
- name: go-grpc
|
||||
out: .
|
||||
opt:
|
||||
- paths=source_relative
|
||||
- require_unimplemented_servers=false
|
8
pkg/zero/connect/buf.yaml
Normal file
8
pkg/zero/connect/buf.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
version: v1
|
||||
build: {}
|
||||
lint:
|
||||
use:
|
||||
- DEFAULT
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
83
pkg/zero/connect/client.go
Normal file
83
pkg/zero/connect/client.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package connect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
grpc_backoff "google.golang.org/grpc/backoff"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDialTimeout = time.Hour
|
||||
)
|
||||
|
||||
type client struct {
|
||||
config *Config
|
||||
tokenProvider TokenProviderFn
|
||||
minTokenTTL time.Duration
|
||||
}
|
||||
|
||||
// TokenProviderFn is a function that returns a token that is expected to be valid for at least minTTL
|
||||
type TokenProviderFn func(ctx context.Context, minTTL time.Duration) (string, error)
|
||||
|
||||
// NewAuthorizedConnectClient creates a new gRPC client for the connect service
|
||||
func NewAuthorizedConnectClient(
|
||||
ctx context.Context,
|
||||
endpoint string,
|
||||
tokenProvider TokenProviderFn,
|
||||
) (ConnectClient, error) {
|
||||
cfg, err := NewConfig(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := &client{
|
||||
tokenProvider: tokenProvider,
|
||||
config: cfg,
|
||||
// streaming connection would reset based on token duration,
|
||||
// so we need it be close to max duration 1hr
|
||||
minTokenTTL: time.Minute * 55,
|
||||
}
|
||||
|
||||
grpcConn, err := cc.getGRPCConn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewConnectClient(grpcConn), nil
|
||||
}
|
||||
|
||||
func (c *client) getGRPCConn(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
conn, err := grpc.DialContext(ctx,
|
||||
c.config.GetConnectionURI(),
|
||||
append(c.config.GetDialOptions(),
|
||||
grpc.WithPerRPCCredentials(c),
|
||||
grpc.WithConnectParams(grpc.ConnectParams{
|
||||
Backoff: grpc_backoff.DefaultConfig,
|
||||
// the MinConnectTimeout is confusing and is actually the max timeout as per grpc implementation
|
||||
MinConnectTimeout: c.config.GetDialTimeout(),
|
||||
}),
|
||||
)...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error dialing grpc server: %w", err)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// GetRequestMetadata implements credentials.PerRPCCredentials
|
||||
func (c *client) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
|
||||
token, err := c.tokenProvider(ctx, c.minTokenTTL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]string{
|
||||
"authorization": fmt.Sprintf("Bearer %s", token),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RequireTransportSecurity implements credentials.PerRPCCredentials
|
||||
func (c *client) RequireTransportSecurity() bool {
|
||||
return c.config.RequireTLS()
|
||||
}
|
96
pkg/zero/connect/client_test.go
Normal file
96
pkg/zero/connect/client_test.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package connect_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
"github.com/pomerium/pomerium/pkg/zero/connect"
|
||||
"github.com/pomerium/pomerium/pkg/zero/token"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
endpoint string
|
||||
connectionURI string
|
||||
requireTLS bool
|
||||
expectError bool
|
||||
}{
|
||||
{"http://localhost:8721", "dns:localhost:8721", false, false},
|
||||
{"https://localhost:8721", "dns:localhost:8721", true, false},
|
||||
{"http://localhost:8721/", "dns:localhost:8721", false, false},
|
||||
{"https://localhost:8721/", "dns:localhost:8721", true, false},
|
||||
{"http://localhost", "dns:localhost:80", false, false},
|
||||
{"https://localhost", "dns:localhost:443", true, false},
|
||||
|
||||
{endpoint: "", expectError: true},
|
||||
{endpoint: "http://", expectError: true},
|
||||
{endpoint: "https://", expectError: true},
|
||||
{endpoint: "localhost:8721", expectError: true},
|
||||
{endpoint: "http://localhost:8721/path", expectError: true},
|
||||
{endpoint: "https://localhost:8721/path", expectError: true},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.endpoint, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg, err := connect.NewConfig(tc.endpoint)
|
||||
if tc.expectError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, tc.connectionURI, cfg.GetConnectionURI(), "connection uri")
|
||||
assert.Equal(t, tc.requireTLS, cfg.RequireTLS(), "require tls")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectClient(t *testing.T) {
|
||||
refreshToken := os.Getenv("CONNECT_CLUSTER_IDENTITY_TOKEN")
|
||||
if refreshToken == "" {
|
||||
t.Skip("CONNECT_CLUSTER_IDENTITY_TOKEN not set")
|
||||
}
|
||||
|
||||
connectServerEndpoint := os.Getenv("CONNECT_SERVER_ENDPOINT")
|
||||
if connectServerEndpoint == "" {
|
||||
connectServerEndpoint = "http://localhost:8721"
|
||||
}
|
||||
|
||||
clusterAPIEndpoint := os.Getenv("CLUSTER_API_ENDPOINT")
|
||||
if clusterAPIEndpoint == "" {
|
||||
clusterAPIEndpoint = "http://localhost:8720/cluster/v1"
|
||||
}
|
||||
|
||||
fetcher, err := cluster_api.NewTokenFetcher(clusterAPIEndpoint)
|
||||
require.NoError(t, err, "error creating token fetcher")
|
||||
|
||||
ctx := context.Background()
|
||||
deadline, ok := t.Deadline()
|
||||
if ok {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, deadline.Add(-1*time.Second))
|
||||
t.Cleanup(cancel)
|
||||
}
|
||||
|
||||
tokenCache := token.NewCache(fetcher, refreshToken)
|
||||
|
||||
connectClient, err := connect.NewAuthorizedConnectClient(ctx, connectServerEndpoint, tokenCache.GetToken)
|
||||
require.NoError(t, err, "error creating connect client")
|
||||
|
||||
stream, err := connectClient.Subscribe(ctx, &connect.SubscribeRequest{})
|
||||
require.NoError(t, err, "error subscribing")
|
||||
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
require.NoError(t, err, "error receiving message")
|
||||
t.Log(msg)
|
||||
}
|
||||
}
|
121
pkg/zero/connect/config.go
Normal file
121
pkg/zero/connect/config.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package connect
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
// Config is the configuration for the gRPC client
|
||||
type Config struct {
|
||||
connectionURI string
|
||||
// requireTLS is whether TLS should be used or cleartext
|
||||
requireTLS bool
|
||||
// opts are additional options to pass to the gRPC client
|
||||
opts []grpc.DialOption
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config from an endpoint string, that has to be in a URL format.
|
||||
// The endpoint can be either http:// or https:// that will be used to determine whether TLS should be used.
|
||||
// if port is not specified, it will be inferred from the scheme (80 for http, 443 for https).
|
||||
func NewConfig(endpoint string) (*Config, error) {
|
||||
c := new(Config)
|
||||
err := c.parseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid endpoint: %w", err)
|
||||
}
|
||||
c.buildTLSOptions()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// GetConnectionURI returns connection string conforming to https://github.com/grpc/grpc/blob/master/doc/naming.md
|
||||
func (c *Config) GetConnectionURI() string {
|
||||
return c.connectionURI
|
||||
}
|
||||
|
||||
// GetDialTimeout returns the timeout for the dial operation
|
||||
func (c *Config) GetDialTimeout() time.Duration {
|
||||
return defaultDialTimeout
|
||||
}
|
||||
|
||||
// RequireTLS returns whether TLS should be used or cleartext
|
||||
func (c *Config) RequireTLS() bool {
|
||||
return c.requireTLS
|
||||
}
|
||||
|
||||
// GetDialOptions returns the dial options to pass to the gRPC client
|
||||
func (c *Config) GetDialOptions() []grpc.DialOption {
|
||||
return c.opts
|
||||
}
|
||||
|
||||
func (c *Config) buildTLSOptions() {
|
||||
creds := insecure.NewCredentials()
|
||||
if c.requireTLS {
|
||||
creds = credentials.NewTLS(&tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
})
|
||||
}
|
||||
c.opts = append(c.opts, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
|
||||
func (c *Config) parseEndpoint(endpoint string) error {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing endpoint url: %w", err)
|
||||
}
|
||||
|
||||
if u.Path != "" && u.Path != "/" {
|
||||
return fmt.Errorf("endpoint path is not supported: %s", u.Path)
|
||||
}
|
||||
|
||||
host, port, err := splitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error splitting host and port: %w", err)
|
||||
}
|
||||
|
||||
var requireTLS bool
|
||||
if u.Scheme == "http" {
|
||||
requireTLS = false
|
||||
if port == "" {
|
||||
port = "80"
|
||||
}
|
||||
} else if u.Scheme == "https" {
|
||||
requireTLS = true
|
||||
if port == "" {
|
||||
port = "443"
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unsupported url scheme: %s", u.Scheme)
|
||||
}
|
||||
|
||||
c.connectionURI = fmt.Sprintf("dns:%s:%s", host, port)
|
||||
c.requireTLS = requireTLS
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var rePort = regexp.MustCompile(`:(\d+)$`)
|
||||
|
||||
func splitHostPort(hostport string) (host, port string, err error) {
|
||||
if hostport == "" {
|
||||
return "", "", fmt.Errorf("empty hostport")
|
||||
}
|
||||
if rePort.MatchString(hostport) {
|
||||
host, port, err = net.SplitHostPort(hostport)
|
||||
if host == "" {
|
||||
return "", "", fmt.Errorf("empty host")
|
||||
}
|
||||
if port == "" {
|
||||
return "", "", fmt.Errorf("empty port")
|
||||
}
|
||||
return host, port, err
|
||||
}
|
||||
return hostport, "", nil
|
||||
}
|
384
pkg/zero/connect/connect.pb.go
Normal file
384
pkg/zero/connect/connect.pb.go
Normal file
|
@ -0,0 +1,384 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc (unknown)
|
||||
// source: connect.proto
|
||||
|
||||
package connect
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// SubscribeRequest is used to subscribe to a stream of messages
|
||||
// from the Zero Cloud to the Pomerium Core.
|
||||
//
|
||||
// The Authorization: Bearer header must contain a valid token,
|
||||
// that belongs to a cluster identity with appropriate claims set.
|
||||
type SubscribeRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *SubscribeRequest) Reset() {
|
||||
*x = SubscribeRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_connect_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SubscribeRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SubscribeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SubscribeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_connect_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_connect_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// Message is an aggregate of all possible messages that can be sent
|
||||
// from the cloud to the core in managed mode.
|
||||
type Message struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Message:
|
||||
//
|
||||
// *Message_ConfigUpdated
|
||||
// *Message_BootstrapConfigUpdated
|
||||
Message isMessage_Message `protobuf_oneof:"message"`
|
||||
}
|
||||
|
||||
func (x *Message) Reset() {
|
||||
*x = Message{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_connect_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Message) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Message) ProtoMessage() {}
|
||||
|
||||
func (x *Message) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_connect_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Message.ProtoReflect.Descriptor instead.
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return file_connect_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (m *Message) GetMessage() isMessage_Message {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Message) GetConfigUpdated() *ConfigUpdated {
|
||||
if x, ok := x.GetMessage().(*Message_ConfigUpdated); ok {
|
||||
return x.ConfigUpdated
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Message) GetBootstrapConfigUpdated() *BootstrapConfigUpdated {
|
||||
if x, ok := x.GetMessage().(*Message_BootstrapConfigUpdated); ok {
|
||||
return x.BootstrapConfigUpdated
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isMessage_Message interface {
|
||||
isMessage_Message()
|
||||
}
|
||||
|
||||
type Message_ConfigUpdated struct {
|
||||
ConfigUpdated *ConfigUpdated `protobuf:"bytes,1,opt,name=config_updated,json=configUpdated,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Message_BootstrapConfigUpdated struct {
|
||||
BootstrapConfigUpdated *BootstrapConfigUpdated `protobuf:"bytes,2,opt,name=bootstrap_config_updated,json=bootstrapConfigUpdated,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Message_ConfigUpdated) isMessage_Message() {}
|
||||
|
||||
func (*Message_BootstrapConfigUpdated) isMessage_Message() {}
|
||||
|
||||
// ConfigUpdated is sent when the configuration has been updated
|
||||
// for the connected Pomerium Core deployment
|
||||
type ConfigUpdated struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// version of the configuration changeset
|
||||
ChangesetVersion int64 `protobuf:"varint,1,opt,name=changeset_version,json=changesetVersion,proto3" json:"changeset_version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ConfigUpdated) Reset() {
|
||||
*x = ConfigUpdated{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_connect_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ConfigUpdated) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConfigUpdated) ProtoMessage() {}
|
||||
|
||||
func (x *ConfigUpdated) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_connect_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConfigUpdated.ProtoReflect.Descriptor instead.
|
||||
func (*ConfigUpdated) Descriptor() ([]byte, []int) {
|
||||
return file_connect_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ConfigUpdated) GetChangesetVersion() int64 {
|
||||
if x != nil {
|
||||
return x.ChangesetVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// BootstrapConfigUpdated is sent when the bootstrap configuration has been
|
||||
// updated. Bootstrap configuration is received via cluster API directly, and
|
||||
// does not involve long running operations to construct it, like with a regular
|
||||
// config.
|
||||
type BootstrapConfigUpdated struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *BootstrapConfigUpdated) Reset() {
|
||||
*x = BootstrapConfigUpdated{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_connect_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *BootstrapConfigUpdated) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BootstrapConfigUpdated) ProtoMessage() {}
|
||||
|
||||
func (x *BootstrapConfigUpdated) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_connect_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BootstrapConfigUpdated.ProtoReflect.Descriptor instead.
|
||||
func (*BootstrapConfigUpdated) Descriptor() ([]byte, []int) {
|
||||
return file_connect_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
var File_connect_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_connect_proto_rawDesc = []byte{
|
||||
0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||
0x0d, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x22, 0x12,
|
||||
0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x45,
|
||||
0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75,
|
||||
0x6d, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70,
|
||||
0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x61, 0x0a, 0x18, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72,
|
||||
0x61, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2e, 0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61,
|
||||
0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x48, 0x00,
|
||||
0x52, 0x16, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x22, 0x3c, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65,
|
||||
0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||
0x10, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x22, 0x18, 0x0a, 0x16, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x32, 0x51, 0x0a, 0x07, 0x43,
|
||||
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x46, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
|
||||
0x69, 0x62, 0x65, 0x12, 0x1f, 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x7a,
|
||||
0x65, 0x72, 0x6f, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2e,
|
||||
0x7a, 0x65, 0x72, 0x6f, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, 0x01, 0x42, 0x2f,
|
||||
0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d,
|
||||
0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70,
|
||||
0x6b, 0x67, 0x2f, 0x7a, 0x65, 0x72, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_connect_proto_rawDescOnce sync.Once
|
||||
file_connect_proto_rawDescData = file_connect_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_connect_proto_rawDescGZIP() []byte {
|
||||
file_connect_proto_rawDescOnce.Do(func() {
|
||||
file_connect_proto_rawDescData = protoimpl.X.CompressGZIP(file_connect_proto_rawDescData)
|
||||
})
|
||||
return file_connect_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_connect_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_connect_proto_goTypes = []interface{}{
|
||||
(*SubscribeRequest)(nil), // 0: pomerium.zero.SubscribeRequest
|
||||
(*Message)(nil), // 1: pomerium.zero.Message
|
||||
(*ConfigUpdated)(nil), // 2: pomerium.zero.ConfigUpdated
|
||||
(*BootstrapConfigUpdated)(nil), // 3: pomerium.zero.BootstrapConfigUpdated
|
||||
}
|
||||
var file_connect_proto_depIdxs = []int32{
|
||||
2, // 0: pomerium.zero.Message.config_updated:type_name -> pomerium.zero.ConfigUpdated
|
||||
3, // 1: pomerium.zero.Message.bootstrap_config_updated:type_name -> pomerium.zero.BootstrapConfigUpdated
|
||||
0, // 2: pomerium.zero.Connect.Subscribe:input_type -> pomerium.zero.SubscribeRequest
|
||||
1, // 3: pomerium.zero.Connect.Subscribe:output_type -> pomerium.zero.Message
|
||||
3, // [3:4] is the sub-list for method output_type
|
||||
2, // [2:3] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_connect_proto_init() }
|
||||
func file_connect_proto_init() {
|
||||
if File_connect_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_connect_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SubscribeRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_connect_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Message); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_connect_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ConfigUpdated); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_connect_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*BootstrapConfigUpdated); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_connect_proto_msgTypes[1].OneofWrappers = []interface{}{
|
||||
(*Message_ConfigUpdated)(nil),
|
||||
(*Message_BootstrapConfigUpdated)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_connect_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_connect_proto_goTypes,
|
||||
DependencyIndexes: file_connect_proto_depIdxs,
|
||||
MessageInfos: file_connect_proto_msgTypes,
|
||||
}.Build()
|
||||
File_connect_proto = out.File
|
||||
file_connect_proto_rawDesc = nil
|
||||
file_connect_proto_goTypes = nil
|
||||
file_connect_proto_depIdxs = nil
|
||||
}
|
41
pkg/zero/connect/connect.proto
Normal file
41
pkg/zero/connect/connect.proto
Normal file
|
@ -0,0 +1,41 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package pomerium.zero;
|
||||
option go_package = "github.com/pomerium/pomerium/pkg/zero/connect";
|
||||
|
||||
// SubscribeRequest is used to subscribe to a stream of messages
|
||||
// from the Zero Cloud to the Pomerium Core.
|
||||
//
|
||||
// The Authorization: Bearer header must contain a valid token,
|
||||
// that belongs to a cluster identity with appropriate claims set.
|
||||
message SubscribeRequest {}
|
||||
|
||||
// Message is an aggregate of all possible messages that can be sent
|
||||
// from the cloud to the core in managed mode.
|
||||
message Message {
|
||||
oneof message {
|
||||
ConfigUpdated config_updated = 1;
|
||||
BootstrapConfigUpdated bootstrap_config_updated = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigUpdated is sent when the configuration has been updated
|
||||
// for the connected Pomerium Core deployment
|
||||
message ConfigUpdated {
|
||||
// version of the configuration changeset
|
||||
int64 changeset_version = 1;
|
||||
}
|
||||
|
||||
// BootstrapConfigUpdated is sent when the bootstrap configuration has been
|
||||
// updated. Bootstrap configuration is received via cluster API directly, and
|
||||
// does not involve long running operations to construct it, like with a regular
|
||||
// config.
|
||||
message BootstrapConfigUpdated {}
|
||||
|
||||
// Connect service is used to maintain a persistent connection between the
|
||||
// Pomerium Core and Zero Cloud and receive messages from the cloud.
|
||||
service Connect {
|
||||
// Subscribe is used to send a stream of messages from the Zero Cloud to the
|
||||
// Pomerium Core in managed mode.
|
||||
rpc Subscribe(SubscribeRequest) returns (stream Message);
|
||||
}
|
138
pkg/zero/connect/connect_grpc.pb.go
Normal file
138
pkg/zero/connect/connect_grpc.pb.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc (unknown)
|
||||
// source: connect.proto
|
||||
|
||||
package connect
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
Connect_Subscribe_FullMethodName = "/pomerium.zero.Connect/Subscribe"
|
||||
)
|
||||
|
||||
// ConnectClient is the client API for Connect service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type ConnectClient interface {
|
||||
// Subscribe is used to send a stream of messages from the Zero Cloud to the
|
||||
// Pomerium Core in managed mode.
|
||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Connect_SubscribeClient, error)
|
||||
}
|
||||
|
||||
type connectClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewConnectClient(cc grpc.ClientConnInterface) ConnectClient {
|
||||
return &connectClient{cc}
|
||||
}
|
||||
|
||||
func (c *connectClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Connect_SubscribeClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &Connect_ServiceDesc.Streams[0], Connect_Subscribe_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &connectSubscribeClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Connect_SubscribeClient interface {
|
||||
Recv() (*Message, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type connectSubscribeClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *connectSubscribeClient) Recv() (*Message, error) {
|
||||
m := new(Message)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ConnectServer is the server API for Connect service.
|
||||
// All implementations should embed UnimplementedConnectServer
|
||||
// for forward compatibility
|
||||
type ConnectServer interface {
|
||||
// Subscribe is used to send a stream of messages from the Zero Cloud to the
|
||||
// Pomerium Core in managed mode.
|
||||
Subscribe(*SubscribeRequest, Connect_SubscribeServer) error
|
||||
}
|
||||
|
||||
// UnimplementedConnectServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedConnectServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedConnectServer) Subscribe(*SubscribeRequest, Connect_SubscribeServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
|
||||
}
|
||||
|
||||
// UnsafeConnectServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ConnectServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeConnectServer interface {
|
||||
mustEmbedUnimplementedConnectServer()
|
||||
}
|
||||
|
||||
func RegisterConnectServer(s grpc.ServiceRegistrar, srv ConnectServer) {
|
||||
s.RegisterService(&Connect_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Connect_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SubscribeRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(ConnectServer).Subscribe(m, &connectSubscribeServer{stream})
|
||||
}
|
||||
|
||||
type Connect_SubscribeServer interface {
|
||||
Send(*Message) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type connectSubscribeServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *connectSubscribeServer) Send(m *Message) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
// Connect_ServiceDesc is the grpc.ServiceDesc for Connect service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Connect_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "pomerium.zero.Connect",
|
||||
HandlerType: (*ConnectServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Subscribe",
|
||||
Handler: _Connect_Subscribe_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "connect.proto",
|
||||
}
|
4
pkg/zero/connect/generate.go
Normal file
4
pkg/zero/connect/generate.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Package connect provides the way to listen for updates from the cloud
|
||||
package connect
|
||||
|
||||
//go:generate go run github.com/bufbuild/buf/cmd/buf@v1.28.1 generate --path connect.proto --config buf.yaml
|
251
pkg/zero/download.go
Normal file
251
pkg/zero/download.go
Normal file
|
@ -0,0 +1,251 @@
|
|||
package zero
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/zero/apierror"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
)
|
||||
|
||||
const (
|
||||
maxErrorResponseBodySize = 2 << 14 // 32kb
|
||||
maxUncompressedBlobSize = 2 << 30 // 1gb
|
||||
)
|
||||
|
||||
// DownloadClusterResourceBundle downloads given cluster resource bundle to given writer.
|
||||
func (api *API) DownloadClusterResourceBundle(
|
||||
ctx context.Context,
|
||||
dst io.Writer,
|
||||
id string,
|
||||
current *DownloadConditional,
|
||||
) (*DownloadResult, error) {
|
||||
req, err := api.getDownloadRequest(ctx, id, current)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get download request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := api.cfg.httpClient.Do(req.Request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("do request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotModified {
|
||||
return &DownloadResult{NotModified: true}, nil
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, httpDownloadError(ctx, resp)
|
||||
}
|
||||
|
||||
var r io.Reader = resp.Body
|
||||
if resp.Header.Get("Content-Encoding") == "gzip" {
|
||||
zr, err := gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gzip reader: %w", err)
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
r = io.LimitReader(zr, maxUncompressedBlobSize)
|
||||
}
|
||||
|
||||
_, err = io.Copy(dst, r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("write body: %w", err)
|
||||
}
|
||||
|
||||
updated, err := newConditionalFromResponse(resp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot obtain cache conditions from response: %w", err)
|
||||
}
|
||||
|
||||
return &DownloadResult{
|
||||
DownloadConditional: updated,
|
||||
Metadata: extractMetadata(resp.Header, req.CaptureHeaders),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type downloadRequest struct {
|
||||
*http.Request
|
||||
cluster_api.DownloadCacheEntry
|
||||
}
|
||||
|
||||
func (api *API) getDownloadRequest(ctx context.Context, id string, current *DownloadConditional) (*downloadRequest, error) {
|
||||
params, err := api.getDownloadParams(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get download URL: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, params.URL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new request: %w", err)
|
||||
}
|
||||
req.Header.Set("Accept-Encoding", "gzip")
|
||||
|
||||
err = current.SetHeaders(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set conditional download headers: %w", err)
|
||||
}
|
||||
|
||||
return &downloadRequest{
|
||||
Request: req,
|
||||
DownloadCacheEntry: *params,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *API) getDownloadParams(ctx context.Context, id string) (*cluster_api.DownloadCacheEntry, error) {
|
||||
param, ok := api.downloadURLCache.Get(id, api.cfg.downloadURLCacheTTL)
|
||||
if ok {
|
||||
return param, nil
|
||||
}
|
||||
|
||||
return api.updateBundleDownloadParams(ctx, id)
|
||||
}
|
||||
|
||||
func (api *API) updateBundleDownloadParams(ctx context.Context, id string) (*cluster_api.DownloadCacheEntry, error) {
|
||||
now := time.Now()
|
||||
|
||||
resp, err := apierror.CheckResponse[cluster_api.DownloadBundleResponse](
|
||||
api.cluster.DownloadClusterResourceBundleWithResponse(ctx, id),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get bundle download URL: %w", err)
|
||||
}
|
||||
|
||||
expiresSeconds, err := strconv.ParseInt(resp.ExpiresInSeconds, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse expiration: %w", err)
|
||||
}
|
||||
|
||||
u, err := url.Parse(resp.Url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse url: %w", err)
|
||||
}
|
||||
|
||||
param := cluster_api.DownloadCacheEntry{
|
||||
URL: *u,
|
||||
ExpiresAt: now.Add(time.Duration(expiresSeconds) * time.Second),
|
||||
CaptureHeaders: resp.CaptureMetadataHeaders,
|
||||
}
|
||||
api.downloadURLCache.Set(id, param)
|
||||
return ¶m, nil
|
||||
}
|
||||
|
||||
// DownloadResult contains the result of a download operation
|
||||
type DownloadResult struct {
|
||||
// NotModified is true if the bundle has not been modified
|
||||
NotModified bool
|
||||
// DownloadConditional contains the new conditional
|
||||
*DownloadConditional
|
||||
// Metadata contains the metadata of the downloaded bundle
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
// DownloadConditional contains the conditional headers for a download operation
|
||||
type DownloadConditional struct {
|
||||
ETag string
|
||||
LastModified string
|
||||
}
|
||||
|
||||
// Validate validates the conditional headers
|
||||
func (c *DownloadConditional) Validate() error {
|
||||
if c.ETag == "" && c.LastModified == "" {
|
||||
return fmt.Errorf("either ETag or LastModified must be set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHeaders sets the conditional headers on the given request
|
||||
func (c *DownloadConditional) SetHeaders(req *http.Request) error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
if err := c.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("If-None-Match", c.ETag)
|
||||
req.Header.Set("If-Modified-Since", c.LastModified)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newConditionalFromResponse(resp *http.Response) (*DownloadConditional, error) {
|
||||
c := &DownloadConditional{
|
||||
ETag: resp.Header.Get("ETag"),
|
||||
LastModified: resp.Header.Get("Last-Modified"),
|
||||
}
|
||||
if err := c.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type xmlError struct {
|
||||
XMLName xml.Name `xml:"Error"`
|
||||
Code string `xml:"Code"`
|
||||
Message string `xml:"Message"`
|
||||
Details string `xml:"Details"`
|
||||
}
|
||||
|
||||
func (e xmlError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
func tryXMLError(body []byte) (bool, error) {
|
||||
var xmlErr xmlError
|
||||
err := xml.Unmarshal(body, &xmlErr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unmarshal xml error: %w", err)
|
||||
}
|
||||
|
||||
return true, xmlErr
|
||||
}
|
||||
|
||||
func httpDownloadError(ctx context.Context, resp *http.Response) error {
|
||||
var buf bytes.Buffer
|
||||
_, err := io.Copy(&buf, io.LimitReader(resp.Body, maxErrorResponseBodySize))
|
||||
|
||||
if isXML(resp.Header.Get("Content-Type")) {
|
||||
ok, err := tryXMLError(buf.Bytes())
|
||||
if ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Debug().Err(err).
|
||||
Str("error", resp.Status).
|
||||
Str("body", buf.String()).Msg("bundle download error")
|
||||
|
||||
return fmt.Errorf("download error: %s", resp.Status)
|
||||
}
|
||||
|
||||
// isXML parses content-type for application/xml
|
||||
func isXML(ct string) bool {
|
||||
mediaType, _, err := mime.ParseMediaType(ct)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return mediaType == "application/xml"
|
||||
}
|
||||
|
||||
func extractMetadata(header http.Header, keys []string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, k := range keys {
|
||||
v := header.Get(k)
|
||||
if v != "" {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
100
pkg/zero/token/cache.go
Normal file
100
pkg/zero/token/cache.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Package token provides a thread-safe cache of a authorization token that may be used across http and grpc clients
|
||||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
maxLockWait = 30 * time.Second
|
||||
)
|
||||
|
||||
// Cache is a thread-safe cache of a authorization token
|
||||
// that may be used across http and grpc clients
|
||||
type Cache struct {
|
||||
TimeNow func() time.Time
|
||||
|
||||
refreshToken string
|
||||
fetcher Fetcher
|
||||
|
||||
lock chan struct{}
|
||||
token atomic.Value
|
||||
}
|
||||
|
||||
// Fetcher is a function that fetches a new token
|
||||
type Fetcher func(ctx context.Context, refreshToken string) (*Token, error)
|
||||
|
||||
// Token is a bearer token
|
||||
type Token struct {
|
||||
// Bearer is the bearer token
|
||||
Bearer string
|
||||
// Expires is the time the token expires
|
||||
Expires time.Time
|
||||
}
|
||||
|
||||
// ExpiresAfter returns true if the token expires after the given time
|
||||
func (t *Token) ExpiresAfter(tm time.Time) bool {
|
||||
return t != nil && t.Expires.After(tm)
|
||||
}
|
||||
|
||||
// NewCache creates a new token cache
|
||||
func NewCache(fetcher Fetcher, refreshToken string) *Cache {
|
||||
return &Cache{
|
||||
lock: make(chan struct{}, 1),
|
||||
fetcher: fetcher,
|
||||
refreshToken: refreshToken,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) timeNow() time.Time {
|
||||
if c.TimeNow != nil {
|
||||
return c.TimeNow()
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// GetToken returns the current token if its at least `minTTL` from expiration, or fetches a new one.
|
||||
func (c *Cache) GetToken(ctx context.Context, minTTL time.Duration) (string, error) {
|
||||
minExpiration := c.timeNow().Add(minTTL)
|
||||
|
||||
token, ok := c.token.Load().(*Token)
|
||||
if ok && token.ExpiresAfter(minExpiration) {
|
||||
return token.Bearer, nil
|
||||
}
|
||||
|
||||
return c.forceRefreshToken(ctx, minExpiration)
|
||||
}
|
||||
|
||||
func (c *Cache) forceRefreshToken(ctx context.Context, minExpiration time.Time) (string, error) {
|
||||
select {
|
||||
case c.lock <- struct{}{}:
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
}
|
||||
defer func() {
|
||||
<-c.lock
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, maxLockWait)
|
||||
defer cancel()
|
||||
|
||||
token, ok := c.token.Load().(*Token)
|
||||
if ok && token.ExpiresAfter(minExpiration) {
|
||||
return token.Bearer, nil
|
||||
}
|
||||
|
||||
token, err := c.fetcher(ctx, c.refreshToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
c.token.Store(token)
|
||||
|
||||
if token.Expires.Before(minExpiration) {
|
||||
return "", fmt.Errorf("new token cannot satisfy TTL: %v", minExpiration.Sub(token.Expires))
|
||||
}
|
||||
|
||||
return token.Bearer, nil
|
||||
}
|
65
pkg/zero/token/cache_test.go
Normal file
65
pkg/zero/token/cache_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package token_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/zero/token"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("token expired, fetch new", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var testToken *token.Token
|
||||
var testError error
|
||||
fetcher := func(ctx context.Context, refreshToken string) (*token.Token, error) {
|
||||
if testToken != nil {
|
||||
token := *testToken
|
||||
return &token, nil
|
||||
}
|
||||
return nil, testError
|
||||
}
|
||||
|
||||
c := token.NewCache(fetcher, "test-refresh-token")
|
||||
now := time.Now()
|
||||
c.TimeNow = func() time.Time { return now }
|
||||
|
||||
testToken = &token.Token{"bearer-1", now.Add(time.Hour)}
|
||||
bearer, err := c.GetToken(context.Background(), time.Minute)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bearer-1", bearer)
|
||||
|
||||
now = now.Add(time.Minute * 30)
|
||||
testToken.Bearer = "bearer-2"
|
||||
|
||||
// token is still valid, so we should get the same one
|
||||
bearer, err = c.GetToken(context.Background(), time.Minute*20)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bearer-1", bearer)
|
||||
|
||||
now = now.Add(time.Minute * 30)
|
||||
testToken = &token.Token{"bearer-3", now.Add(time.Hour)}
|
||||
bearer, err = c.GetToken(context.Background(), time.Minute*30)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "bearer-3", bearer)
|
||||
})
|
||||
|
||||
t.Run("token cannot fit minTTL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fetcher := func(ctx context.Context, refreshToken string) (*token.Token, error) {
|
||||
return &token.Token{"ok-bearer", time.Now().Add(time.Minute)}, nil
|
||||
}
|
||||
|
||||
c := token.NewCache(fetcher, "test-refresh-token")
|
||||
_, err := c.GetToken(context.Background(), time.Minute*2)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue