Compare commits

...

11 Commits

Author SHA1 Message Date
1ffec1f173 feat(layer,queue): prevent browser caching for queue page
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-05 13:35:21 -06:00
aab5452fa2 feat: sentry integration
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
ref #3
2023-07-05 12:05:30 -06:00
a176b754cd feat: add queue adapter tests
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-05 08:55:15 -06:00
7b04eb2418 fix(doc): bad link
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-05 15:14:27 +02:00
f8d9ff15b5 doc: add link to misc/docker-compose
Some checks reported errors
Cadoles/bouncer/pipeline/head Something is wrong with the build of this commit
2023-07-05 15:13:31 +02:00
5bd7cbc132 fix(docker): move templates to expected path
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-03 19:42:44 -06:00
1b06f07ce8 feat: update packaged configuration
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-01 13:55:26 -06:00
82228fd115 feat: allow customization of proxy transport configuration
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-01 13:43:18 -06:00
15daddbe13 feat: add multi-nodes docker-compose deployment example
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-01 11:38:16 -06:00
5a7062d53e fix: remove log message
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-07-01 11:33:59 -06:00
74409f18e8 feat: update packaged configuration
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good
2023-06-30 17:51:01 -06:00
30 changed files with 805 additions and 80 deletions

View File

@ -3,16 +3,30 @@ FROM golang:1.20 AS BUILD
RUN apt-get update \
&& apt-get install -y make
ARG YQ_VERSION=4.34.1
RUN mkdir -p /usr/local/bin \
&& wget -O /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64 \
&& chmod +x /usr/local/bin/yq
COPY . /src
WORKDIR /src
RUN make GORELEASER_ARGS='build --rm-dist --single-target --snapshot' goreleaser
FROM busybox:latest AS RUNTIME
# Patch config
RUN /src/dist/bouncer_linux_amd64_v1/bouncer -c '' config dump > /src/dist/bouncer_linux_amd64_v1/config.yml \
&& yq -i '.layers.queue.templateDir = "/usr/share/bouncer/layers/queue/templates"' /src/dist/bouncer_linux_amd64_v1/config.yml \
&& yq -i '.admin.auth.privateKey = "/etc/bouncer/admin-key.json"' /src/dist/bouncer_linux_amd64_v1/config.yml \
&& yq -i '.redis.adresses = ["redis:6379"]' /src/dist/bouncer_linux_amd64_v1/config.yml
FROM alpine:3.18 AS RUNTIME
ARG DUMB_INIT_VERSION=1.2.5
RUN apk add --no-cache ca-certificates
RUN mkdir -p /usr/local/bin \
&& wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64 \
&& chmod +x /usr/local/bin/dumb-init
@ -22,15 +36,14 @@ ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
RUN mkdir -p /usr/local/bin /usr/share/bouncer/bin /etc/bouncer
COPY --from=BUILD /src/dist/bouncer_linux_amd64_v1/bouncer /usr/share/bouncer/bin/bouncer
COPY --from=BUILD /src/layers /usr/share/bouncer/
COPY --from=BUILD /src/layers /usr/share/bouncer/layers
COPY --from=BUILD /src/dist/bouncer_linux_amd64_v1/config.yml /etc/bouncer/config.yml
RUN ln -s /usr/share/bouncer/bin/bouncer /usr/local/bin/bouncer \
&& /usr/share/bouncer/bin/bouncer -c '' config dump > /etc/bouncer/config.yml
RUN ln -s /usr/share/bouncer/bin/bouncer /usr/local/bin/bouncer
EXPOSE 8080
EXPOSE 8081
ENV BOUNCER_WORKDIR=/usr/share/bouncer
ENV BOUNCER_CONFIG=/etc/bouncer/config.yml
CMD ["bouncer"]

View File

@ -75,9 +75,11 @@ finish-release:
docker-build:
docker build -t $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) .
docker tag $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) $(DOCKER_IMAGE_NAME):latest
docker-release:
docker push $(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)
docker push $(DOCKER_IMAGE_NAME):latest
gitea-release: tools/gitea-release/bin/gitea-release.sh goreleaser
mkdir -p .gitea-release

View File

@ -3,6 +3,9 @@
- [(FR) - Premiers pas](./fr/getting-started.md)
- [(FR) - Architecture générale](./fr/general-architecture.md)
## Exemples
- [(FR) - Exemple de déploiement multi-noeuds](../misc/docker-compose/README.md)
## Référence
- [(FR) - Layers](./fr/references/layers/README.md)

18
go.mod
View File

@ -3,13 +3,15 @@ module forge.cadoles.com/cadoles/bouncer
go 1.20
require (
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230512083245-e2dc3e1a0333
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6
github.com/Masterminds/sprig/v3 v3.2.3
github.com/btcsuite/btcd/btcutil v1.1.3
github.com/getsentry/sentry-go v0.22.0
github.com/go-chi/chi/v5 v5.0.8
github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/mitchellh/mapstructure v1.4.1
github.com/ory/dockertest/v3 v3.10.0
github.com/prometheus/client_golang v1.16.0
github.com/qri-io/jsonschema v0.2.1
github.com/redis/go-redis/v9 v9.0.4
)
@ -32,11 +34,10 @@ require (
github.com/docker/go-units v0.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
@ -45,7 +46,6 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
@ -53,14 +53,14 @@ require (
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
@ -72,12 +72,12 @@ require (
github.com/dlclark/regexp2 v1.9.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/go-chi/cors v1.2.1
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/uuid v1.3.0
github.com/leodido/go-urn v1.1.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect

37
go.sum
View File

@ -49,8 +49,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230512083245-e2dc3e1a0333 h1:dAajr9wX8WuFPrwjbKNXRmbF+4AaAT7bUj66G7gdZ+c=
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230512083245-e2dc3e1a0333/go.mod h1:o8ZK5v/3J1dRmklFVn1l6WHAyQ3LgegyHjRIT8KLAFw=
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6 h1:FTk0ZoaV5N8Tkps5Da5RrDMZZXSHZIuD67Hy1Y4fsos=
forge.cadoles.com/Cadoles/go-proxy v0.0.0-20230701194111-c6b3d482cca6/go.mod h1:o8ZK5v/3J1dRmklFVn1l6WHAyQ3LgegyHjRIT8KLAFw=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -178,19 +178,24 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getsentry/sentry-go v0.22.0 h1:XNX9zKbv7baSEI65l+H1GEJgSeIC1c7EN5kluWaP6dM=
github.com/getsentry/sentry-go v0.22.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@ -230,7 +235,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@ -252,7 +256,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -311,15 +314,14 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
@ -386,7 +388,7 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
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=
@ -413,8 +415,6 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -426,8 +426,9 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@ -607,8 +608,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -674,14 +675,13 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -704,6 +704,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -908,14 +909,12 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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-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=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=

View File

@ -1,11 +1,14 @@
package admin
import (
"context"
"fmt"
"net/http"
"forge.cadoles.com/cadoles/bouncer/internal/schema"
"github.com/getsentry/sentry-go"
"gitlab.com/wpetit/goweb/api"
"gitlab.com/wpetit/goweb/logger"
)
const ErrCodeAlreadyExist api.ErrorCode = "already-exist"
@ -29,3 +32,8 @@ func invalidDataErrorResponse(w http.ResponseWriter, r *http.Request, err *schem
return
}
func logAndCaptureError(ctx context.Context, message string, err error) {
sentry.CaptureException(err)
logger.Error(ctx, message, logger.E(err))
}

View File

@ -1,6 +1,7 @@
package admin
import (
"fmt"
"net/http"
"sort"
@ -10,7 +11,6 @@ import (
"github.com/go-chi/chi/v5"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/api"
"gitlab.com/wpetit/goweb/logger"
)
type QueryLayerResponse struct {
@ -38,7 +38,7 @@ func (s *Server) queryLayer(w http.ResponseWriter, r *http.Request) {
options...,
)
if err != nil {
logger.Error(ctx, "could not list layers", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not list layers", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -85,7 +85,7 @@ func (s *Server) getLayer(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not get layer", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not get layer", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -120,7 +120,7 @@ func (s *Server) deleteLayer(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not delete layer", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not delete layer", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -156,7 +156,7 @@ func (s *Server) createLayer(w http.ResponseWriter, r *http.Request) {
layerName, err := store.ValidateName(createLayerReq.Name)
if err != nil {
logger.Error(r.Context(), "invalid 'name' parameter", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "invalid 'name' parameter", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, nil)
return
@ -165,7 +165,7 @@ func (s *Server) createLayer(w http.ResponseWriter, r *http.Request) {
layerType := store.LayerType(createLayerReq.Type)
if !setup.LayerTypeExists(layerType) {
logger.Error(r.Context(), "unknown layer type", logger.E(errors.WithStack(err)), logger.F("layerType", layerType))
logAndCaptureError(ctx, fmt.Sprintf("unknown layer type '%s'", layerType), errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeInvalidRequest, nil)
return
@ -179,7 +179,7 @@ func (s *Server) createLayer(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not create layer", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not create layer", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -223,7 +223,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not get layer", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not get layer", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -247,7 +247,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
if updateLayerReq.Options != nil {
layerOptionsSchema, err := setup.GetLayerOptionsSchema(layer.Type)
if err != nil {
logger.Error(r.Context(), "could not retrieve layer options schema", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not retrieve layer options schema", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -258,7 +258,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
}(updateLayerReq.Options)
if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil {
logger.Error(r.Context(), "could not validate layer options", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not validate layer options", errors.WithStack(err))
var invalidDataErr *schema.InvalidDataError
if errors.As(err, &invalidDataErr) {
@ -286,7 +286,7 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not update layer", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not update layer", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -300,21 +300,7 @@ func getLayerName(w http.ResponseWriter, r *http.Request) (store.LayerName, bool
name, err := store.ValidateName(rawLayerName)
if err != nil {
logger.Error(r.Context(), "could not parse layer name", logger.E(errors.WithStack(err)))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return "", false
}
return store.LayerName(name), true
}
func geLayerName(w http.ResponseWriter, r *http.Request) (store.LayerName, bool) {
rawLayerName := chi.URLParam(r, "layerName")
name, err := store.ValidateName(rawLayerName)
if err != nil {
logger.Error(r.Context(), "could not parse layer name", logger.E(errors.WithStack(err)))
logAndCaptureError(r.Context(), "could not parse layer name", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return "", false

View File

@ -11,7 +11,6 @@ import (
"github.com/go-chi/chi/v5"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/api"
"gitlab.com/wpetit/goweb/logger"
)
type QueryProxyResponse struct {
@ -37,7 +36,7 @@ func (s *Server) queryProxy(w http.ResponseWriter, r *http.Request) {
options...,
)
if err != nil {
logger.Error(ctx, "could not list proxies", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not list proxies", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -79,7 +78,7 @@ func (s *Server) getProxy(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not get proxy", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not get proxy", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -109,7 +108,7 @@ func (s *Server) deleteProxy(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not delete proxy", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not delete proxy", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -140,14 +139,14 @@ func (s *Server) createProxy(w http.ResponseWriter, r *http.Request) {
name, err := store.ValidateName(createProxyReq.Name)
if err != nil {
logger.Error(r.Context(), "could not parse 'name' parameter", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not parse 'name' parameter", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return
}
if _, err := url.Parse(createProxyReq.To); err != nil {
logger.Error(r.Context(), "could not parse 'to' parameter", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not parse 'to' parameter", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return
@ -161,7 +160,7 @@ func (s *Server) createProxy(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not create proxy", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not create proxy", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -207,7 +206,7 @@ func (s *Server) updateProxy(w http.ResponseWriter, r *http.Request) {
if updateProxyReq.To != nil {
_, err := url.Parse(*updateProxyReq.To)
if err != nil {
logger.Error(r.Context(), "could not parse 'to' parameter", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not parse 'to' parameter", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return
@ -235,7 +234,7 @@ func (s *Server) updateProxy(w http.ResponseWriter, r *http.Request) {
return
}
logger.Error(ctx, "could not update proxy", logger.E(errors.WithStack(err)))
logAndCaptureError(ctx, "could not update proxy", errors.WithStack(err))
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
return
@ -249,7 +248,7 @@ func getProxyName(w http.ResponseWriter, r *http.Request) (store.ProxyName, bool
name, err := store.ValidateName(rawProxyName)
if err != nil {
logger.Error(r.Context(), "could not parse proxy name", logger.E(errors.WithStack(err)))
logAndCaptureError(r.Context(), "could not parse proxy name", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return "", false
@ -263,7 +262,7 @@ func getIntQueryParam(w http.ResponseWriter, r *http.Request, param string, defa
if rawValue != "" {
value, err := strconv.ParseInt(rawValue, 10, 64)
if err != nil {
logger.Error(r.Context(), "could not parse int param", logger.F("param", param), logger.E(errors.WithStack(err)))
logAndCaptureError(r.Context(), "could not parse int param", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return 0, false
@ -296,7 +295,7 @@ func getStringableSliceValues[T ~string](w http.ResponseWriter, r *http.Request,
for _, rv := range rawValues {
v, err := validate(rv)
if err != nil {
logger.Error(r.Context(), "could not parse ids slice param", logger.F("param", param), logger.E(errors.WithStack(err)))
logAndCaptureError(r.Context(), "could not parse ids slice param", errors.WithStack(err))
api.ErrorResponse(w, http.StatusBadRequest, api.ErrCodeMalformedRequest, nil)
return nil, false

View File

@ -12,6 +12,7 @@ import (
"forge.cadoles.com/cadoles/bouncer/internal/config"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"forge.cadoles.com/cadoles/bouncer/internal/store"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
@ -92,6 +93,16 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
router.Use(middleware.Logger)
if s.serverConfig.Sentry.DSN != "" {
logger.Info(ctx, "enabling sentry http middleware")
sentryMiddleware := sentryhttp.New(sentryhttp.Options{
Repanic: true,
})
router.Use(sentryMiddleware.Handle)
}
corsMiddleware := cors.New(cors.Options{
AllowedOrigins: s.serverConfig.CORS.AllowedOrigins,
AllowedMethods: s.serverConfig.CORS.AllowedMethods,

View File

@ -7,9 +7,9 @@ import (
"sort"
"time"
"github.com/getsentry/sentry-go"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"gitlab.com/wpetit/goweb/logger"
)
func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands ...*cli.Command) {
@ -30,8 +30,6 @@ func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands
workdir := ctx.String("workdir")
// Switch to new working directory if defined
if workdir != "" {
logger.Info(ctx.Context, "changing working directory", logger.F("workdir", workdir))
if err := os.Chdir(workdir); err != nil {
return errors.Wrap(err, "could not change working directory")
}
@ -93,6 +91,8 @@ func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands
return
}
sentry.CaptureException(err)
debug := ctx.Bool("debug")
if !debug {

View File

@ -6,6 +6,7 @@ import (
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/command/common"
"forge.cadoles.com/cadoles/bouncer/internal/setup"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"gitlab.com/wpetit/goweb/logger"
@ -27,6 +28,14 @@ func RunCommand() *cli.Command {
logger.SetFormat(logger.Format(conf.Logger.Format))
logger.SetLevel(logger.Level(conf.Logger.Level))
projectVersion := ctx.String("projectVersion")
flushSentry, err := setup.SetupSentry(ctx.Context, conf.Admin.Sentry, projectVersion)
if err != nil {
return errors.Wrap(err, "could not initialize sentry client")
}
defer flushSentry()
srv := admin.NewServer(
admin.WithServerConfig(conf.Admin),
admin.WithRedisConfig(conf.Redis),

View File

@ -28,6 +28,14 @@ func RunCommand() *cli.Command {
logger.SetFormat(logger.Format(conf.Logger.Format))
logger.SetLevel(logger.Level(conf.Logger.Level))
projectVersion := ctx.String("projectVersion")
flushSentry, err := setup.SetupSentry(ctx.Context, conf.Proxy.Sentry, projectVersion)
if err != nil {
return errors.Wrap(err, "could not initialize sentry client")
}
defer flushSentry()
layers, err := setup.GetLayers(ctx.Context, conf)
if err != nil {
return errors.Wrap(err, "could not initialize director layers")

View File

@ -5,6 +5,7 @@ type AdminServerConfig struct {
CORS CORSConfig `yaml:"cors"`
Auth AuthConfig `yaml:"auth"`
Metrics MetricsConfig `yaml:"metrics"`
Sentry SentryConfig `yaml:"sentry"`
}
func NewDefaultAdminServerConfig() AdminServerConfig {
@ -13,6 +14,7 @@ func NewDefaultAdminServerConfig() AdminServerConfig {
CORS: NewDefaultCORSConfig(),
Auth: NewDefaultAuthConfig(),
Metrics: NewDefaultMetricsConfig(),
Sentry: NewDefaultSentryConfig(),
}
}

View File

@ -53,6 +53,29 @@ func (ii *InterpolatedInt) UnmarshalYAML(value *yaml.Node) error {
return nil
}
type InterpolatedFloat float64
func (ifl *InterpolatedFloat) UnmarshalYAML(value *yaml.Node) error {
var str string
if err := value.Decode(&str); err != nil {
return errors.Wrapf(err, "could not decode value '%v' (line '%d') into string", value.Value, value.Line)
}
if match := reVar.FindStringSubmatch(str); len(match) > 0 {
str = os.Getenv(match[1])
}
floatVal, err := strconv.ParseFloat(str, 10)
if err != nil {
return errors.Wrapf(err, "could not parse float '%v', line '%d'", str, value.Line)
}
*ifl = InterpolatedFloat(floatVal)
return nil
}
type InterpolatedBool bool
func (ib *InterpolatedBool) UnmarshalYAML(value *yaml.Node) error {

View File

@ -1,13 +1,73 @@
package config
import "time"
type ProxyServerConfig struct {
HTTP HTTPConfig `yaml:"http"`
Metrics MetricsConfig `yaml:"metrics"`
HTTP HTTPConfig `yaml:"http"`
Metrics MetricsConfig `yaml:"metrics"`
Transport TransportConfig `yaml:"transport"`
Dial DialConfig `yaml:"dial"`
Sentry SentryConfig `yaml:"sentry"`
}
// See https://pkg.go.dev/net/http#Transport
type TransportConfig struct {
ForceAttemptHTTP2 InterpolatedBool `yaml:"forceAttemptHTTP2"`
MaxIdleConns InterpolatedInt `yaml:"maxIdleConns"`
MaxIdleConnsPerHost InterpolatedInt `yaml:"maxIdleConnsPerHost"`
MaxConnsPerHost InterpolatedInt `yaml:"maxConnsPerHost"`
IdleConnTimeout *InterpolatedDuration `yaml:"idleConnTimeout"`
TLSHandshakeTimeout *InterpolatedDuration `yaml:"tlsHandshakeTimeout"`
ExpectContinueTimeout *InterpolatedDuration `yaml:"expectContinueTimeout"`
DisableKeepAlives InterpolatedBool `yaml:"disableKeepAlives"`
DisableCompression InterpolatedBool `yaml:"disableCompression"`
ResponseHeaderTimeout *InterpolatedDuration `yaml:"responseHeaderTimeout"`
WriteBufferSize InterpolatedInt `yaml:"writeBufferSize"`
ReadBufferSize InterpolatedInt `yaml:"readBufferSize"`
MaxResponseHeaderBytes InterpolatedInt `yaml:"maxResponseHeaderBytes"`
}
func NewDefaultProxyServerConfig() ProxyServerConfig {
return ProxyServerConfig{
HTTP: NewHTTPConfig("0.0.0.0", 8080),
Metrics: NewDefaultMetricsConfig(),
HTTP: NewHTTPConfig("0.0.0.0", 8080),
Metrics: NewDefaultMetricsConfig(),
Transport: NewDefaultTransportConfig(),
Dial: NewDefaultDialConfig(),
Sentry: NewDefaultSentryConfig(),
}
}
// See https://pkg.go.dev/net#Dialer
type DialConfig struct {
Timeout *InterpolatedDuration `yaml:"timeout"`
KeepAlive *InterpolatedDuration `yaml:"keepAlive"`
FallbackDelay *InterpolatedDuration `yaml:"fallbackDelay"`
DualStack InterpolatedBool `yaml:"dualStack"`
}
func NewDefaultDialConfig() DialConfig {
return DialConfig{
Timeout: NewInterpolatedDuration(30 * time.Second),
KeepAlive: NewInterpolatedDuration(30 * time.Second),
FallbackDelay: NewInterpolatedDuration(300 * time.Millisecond),
DualStack: true,
}
}
func NewDefaultTransportConfig() TransportConfig {
return TransportConfig{
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
MaxConnsPerHost: 100,
IdleConnTimeout: NewInterpolatedDuration(90 * time.Second),
TLSHandshakeTimeout: NewInterpolatedDuration(10 * time.Second),
ExpectContinueTimeout: NewInterpolatedDuration(1 * time.Second),
ResponseHeaderTimeout: NewInterpolatedDuration(10 * time.Second),
DisableCompression: false,
DisableKeepAlives: false,
ReadBufferSize: 4096,
WriteBufferSize: 4096,
MaxResponseHeaderBytes: 0,
}
}

43
internal/config/sentry.go Normal file
View File

@ -0,0 +1,43 @@
package config
import "time"
// Sentry configuration
// See https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions
type SentryConfig struct {
DSN InterpolatedString `yaml:"dsn"`
Debug InterpolatedBool `yaml:"debug"`
FlushTimeout *InterpolatedDuration `yaml:"flushTimeout"`
AttachStacktrace InterpolatedBool `yaml:"attachStacktrace"`
SampleRate InterpolatedFloat `yaml:"sampleRate"`
EnableTracing InterpolatedBool `yaml:"enableTracing"`
TracesSampleRate InterpolatedFloat `yaml:"tracesSampleRate"`
ProfilesSampleRate InterpolatedFloat `yaml:"profilesSampleRate"`
IgnoreErrors InterpolatedStringSlice `yaml:"ignoreErrors"`
SendDefaultPII InterpolatedBool `yaml:"sendDefaultPII"`
ServerName InterpolatedString `yaml:"serverName"`
Environment InterpolatedString `yaml:"environment"`
MaxBreadcrumbs InterpolatedInt `yaml:"maxBreadcrumbs"`
MaxSpans InterpolatedInt `yaml:"maxSpans"`
MaxErrorDepth InterpolatedInt `yaml:"maxErrorDepth"`
}
func NewDefaultSentryConfig() SentryConfig {
return SentryConfig{
DSN: "",
Debug: false,
FlushTimeout: NewInterpolatedDuration(2 * time.Second),
AttachStacktrace: true,
SampleRate: 1,
EnableTracing: true,
TracesSampleRate: 0.2,
ProfilesSampleRate: 1,
IgnoreErrors: []string{},
SendDefaultPII: false,
ServerName: "",
Environment: "",
MaxBreadcrumbs: 0,
MaxSpans: 1000,
MaxErrorDepth: 10,
}
}

43
internal/logger/writer.go Normal file
View File

@ -0,0 +1,43 @@
package logger
import (
"context"
"io"
"gitlab.com/wpetit/goweb/logger"
)
type Writer struct {
ctx context.Context
level logger.Level
}
// Write implements io.Writer.
func (w *Writer) Write(p []byte) (n int, err error) {
w.log(string(p))
return len(p), nil
}
func (w *Writer) log(message string) {
switch w.level {
case logger.LevelDebug:
logger.Debug(w.ctx, message)
case logger.LevelInfo:
logger.Info(w.ctx, message)
case logger.LevelWarn:
logger.Warn(w.ctx, message)
case logger.LevelError:
logger.Error(w.ctx, message)
case logger.LevelCritical:
logger.Critical(w.ctx, message)
default:
logger.Debug(w.ctx, message)
}
}
func NewWriter(ctx context.Context, level logger.Level) *Writer {
return &Writer{ctx, level}
}
var _ io.Writer = &Writer{}

View File

@ -7,6 +7,7 @@ import (
"math/rand"
"net/http"
"path/filepath"
"strconv"
"sync"
"sync/atomic"
"time"
@ -181,6 +182,8 @@ func (q *Queue) renderQueuePage(w http.ResponseWriter, r *http.Request, queueNam
RefreshRate: refreshRate,
}
w.Header().Add("Cache-Control", "no-cache")
w.Header().Add("Retry-After", strconv.FormatInt(int64(refreshRate.Seconds()), 10))
w.WriteHeader(http.StatusServiceUnavailable)
if err := q.tmpl.ExecuteTemplate(w, "queue", templateData); err != nil {

View File

@ -30,7 +30,7 @@ func (a *Adapter) Refresh(ctx context.Context, queueName string, keepAlive time.
cmd := tx.ZRangeByScore(ctx, lastSeenKey, &redis.ZRangeBy{
Min: "0",
Max: strconv.FormatInt(expires.Unix(), 10),
Max: strconv.FormatInt(expires.UnixNano(), 10),
})
members, err := cmd.Result()
@ -75,7 +75,7 @@ func (a *Adapter) Touch(ctx context.Context, queueName string, sessionId string)
for retry > 0 {
err := withTx(ctx, a.client, func(ctx context.Context, tx *redis.Tx) error {
now := time.Now().UTC().Unix()
now := time.Now().UTC().UnixNano()
err := tx.ZAddNX(ctx, rankKey, redis.Z{Score: float64(now), Member: sessionId}).Err()
if err != nil {

View File

@ -0,0 +1,12 @@
package redis
import (
"testing"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue/testsuite"
)
func TestAdapter(t *testing.T) {
adapter := NewAdapter(client, 3)
testsuite.TestAdapter(t, adapter)
}

View File

@ -0,0 +1,58 @@
package redis
import (
"context"
"log"
"os"
"testing"
"github.com/ory/dockertest/v3"
"github.com/pkg/errors"
"github.com/redis/go-redis/v9"
)
var client redis.UniversalClient
func TestMain(m *testing.M) {
// uses a sensible default on windows (tcp/http) and linux/osx (socket)
pool, err := dockertest.NewPool("")
if err != nil {
log.Fatalf("%+v", errors.WithStack(err))
}
// uses pool to try to connect to Docker
err = pool.Client.Ping()
if err != nil {
log.Fatalf("%+v", errors.WithStack(err))
}
// pulls an image, creates a container based on it and runs it
resource, err := pool.Run("redis", "alpine3.17", []string{})
if err != nil {
log.Fatalf("%+v", errors.WithStack(err))
}
if err := pool.Retry(func() error {
client = redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{resource.GetHostPort("6379/tcp")},
})
ctx := context.Background()
if cmd := client.Ping(ctx); cmd.Err() != nil {
return errors.WithStack(err)
}
return nil
}); err != nil {
log.Fatalf("%+v", errors.WithStack(err))
}
code := m.Run()
if err := pool.Purge(resource); err != nil {
log.Fatalf("%+v", errors.WithStack(err))
}
os.Exit(code)
}

View File

@ -0,0 +1,99 @@
package testsuite
import (
"context"
"fmt"
"testing"
"time"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue"
"github.com/pkg/errors"
)
type adapterTestCase struct {
Name string
Do func(adapter queue.Adapter) error
}
var adapterTestCases = []adapterTestCase{
{
Name: "Test queue ranking",
Do: func(adapter queue.Adapter) error {
ctx := context.Background()
queueName := "test_queue_ranking"
sessionIdPattern := "session-%d"
totalSessions := int64(100)
for idx := int64(0); idx < totalSessions; idx++ {
sessionId := fmt.Sprintf(sessionIdPattern, idx)
rank, err := adapter.Touch(ctx, queueName, sessionId)
if err != nil {
return errors.Wrapf(err, "could not touch session '%s' (index: %d, rank: %d)", sessionId, idx, rank)
}
if e, g := int64(idx), rank; e != g {
return errors.Errorf("rank('%s'): expected '%v', got '%v'", sessionId, e, g)
}
}
status, err := adapter.Status(ctx, queueName)
if err != nil {
return errors.Wrap(err, "could not retrieve queue status")
}
if e, g := totalSessions, status.Sessions; e != g {
return errors.Errorf("status.Sessions: expected '%v', got '%v'", e, g)
}
return nil
},
},
{
Name: "Test session expiration",
Do: func(adapter queue.Adapter) error {
ctx := context.Background()
queueName := "test_session_expiration"
sessionId := "session-1"
rank, err := adapter.Touch(ctx, queueName, sessionId)
if err != nil {
return errors.Wrapf(err, "could not touch session '%s'", sessionId)
}
if e, g := int64(0), rank; e != g {
return errors.Errorf("rank('%s'): expected '%v', got '%v'", sessionId, e, g)
}
<-time.After(time.Second)
if err := adapter.Refresh(ctx, queueName, time.Second); err != nil {
return errors.Wrap(err, "could not refresh queue")
}
status, err := adapter.Status(ctx, queueName)
if err != nil {
return errors.Wrap(err, "could not retrieve queue status")
}
if e, g := int64(0), status.Sessions; e != g {
return errors.Errorf("status.Sessions: expected '%v', got '%v'", e, g)
}
return nil
},
},
}
func TestAdapter(t *testing.T, adapter queue.Adapter) {
for _, tc := range adapterTestCases {
func(tc adapterTestCase) {
t.Run(tc.Name, func(t *testing.T) {
if err := tc.Do(adapter); err != nil {
t.Fatalf("%+v", errors.WithStack(err))
}
})
}(tc)
}
}

View File

@ -6,12 +6,17 @@ import (
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"time"
"forge.cadoles.com/Cadoles/go-proxy"
bouncerChi "forge.cadoles.com/cadoles/bouncer/internal/chi"
"forge.cadoles.com/cadoles/bouncer/internal/config"
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/getsentry/sentry-go"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/pkg/errors"
@ -86,6 +91,16 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
router.Use(middleware.RequestLogger(bouncerChi.NewLogFormatter()))
if s.serverConfig.Sentry.DSN != "" {
logger.Info(ctx, "enabling sentry http middleware")
sentryMiddleware := sentryhttp.New(sentryhttp.Options{
Repanic: true,
})
router.Use(sentryMiddleware.Handle)
}
if s.serverConfig.Metrics.Enabled {
metrics := s.serverConfig.Metrics
@ -115,6 +130,7 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
proxy.WithResponseTransformers(
director.ResponseTransformer(),
),
proxy.WithReverseProxyFactory(s.createReverseProxy),
)
r.Handle("/*", handler)
@ -127,6 +143,52 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
logger.Info(ctx, "http server exiting")
}
func (s *Server) createReverseProxy(ctx context.Context, target *url.URL) *httputil.ReverseProxy {
reverseProxy := httputil.NewSingleHostReverseProxy(target)
dialConfig := s.serverConfig.Dial
dialer := &net.Dialer{
Timeout: time.Duration(*dialConfig.Timeout),
KeepAlive: time.Duration(*dialConfig.KeepAlive),
FallbackDelay: time.Duration(*dialConfig.FallbackDelay),
DualStack: bool(dialConfig.DualStack),
}
transportConfig := s.serverConfig.Transport
reverseProxy.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
ForceAttemptHTTP2: bool(transportConfig.ForceAttemptHTTP2),
MaxIdleConns: int(transportConfig.MaxIdleConns),
MaxIdleConnsPerHost: int(transportConfig.MaxIdleConnsPerHost),
MaxConnsPerHost: int(transportConfig.MaxConnsPerHost),
IdleConnTimeout: time.Duration(*transportConfig.IdleConnTimeout),
TLSHandshakeTimeout: time.Duration(*transportConfig.TLSHandshakeTimeout),
ExpectContinueTimeout: time.Duration(*transportConfig.ExpectContinueTimeout),
DisableKeepAlives: bool(transportConfig.DisableKeepAlives),
DisableCompression: bool(transportConfig.DisableCompression),
ResponseHeaderTimeout: time.Duration(*transportConfig.ResponseHeaderTimeout),
WriteBufferSize: int(transportConfig.WriteBufferSize),
ReadBufferSize: int(transportConfig.ReadBufferSize),
MaxResponseHeaderBytes: int64(transportConfig.MaxResponseHeaderBytes),
}
reverseProxy.ErrorHandler = s.errorHandler
return reverseProxy
}
func (s *Server) errorHandler(w http.ResponseWriter, r *http.Request, err error) {
err = errors.WithStack(err)
logger.Error(r.Context(), "proxy error", logger.E(err))
sentry.CaptureException(err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
func NewServer(funcs ...OptionFunc) *Server {
opt := defaultOption()
for _, fn := range funcs {

42
internal/setup/sentry.go Normal file
View File

@ -0,0 +1,42 @@
package setup
import (
"context"
"time"
"forge.cadoles.com/cadoles/bouncer/internal/config"
loggerWriter "forge.cadoles.com/cadoles/bouncer/internal/logger"
"github.com/getsentry/sentry-go"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
func SetupSentry(ctx context.Context, conf config.SentryConfig, release string) (func(), error) {
err := sentry.Init(sentry.ClientOptions{
Dsn: string(conf.DSN),
Debug: bool(conf.Debug),
AttachStacktrace: bool(conf.AttachStacktrace),
SampleRate: float64(conf.SampleRate),
EnableTracing: bool(conf.EnableTracing),
TracesSampleRate: float64(conf.TracesSampleRate),
ProfilesSampleRate: float64(conf.ProfilesSampleRate),
IgnoreErrors: conf.IgnoreErrors,
SendDefaultPII: bool(conf.SendDefaultPII),
ServerName: string(conf.ServerName),
Release: release,
Environment: string(conf.Environment),
MaxBreadcrumbs: int(conf.MaxBreadcrumbs),
MaxSpans: int(conf.MaxSpans),
MaxErrorDepth: int(conf.MaxErrorDepth),
DebugWriter: loggerWriter.NewWriter(ctx, logger.LevelDebug),
})
if err != nil {
return nil, errors.WithStack(err)
}
flush := func() {
sentry.Flush(time.Duration(*conf.FlushTimeout))
}
return flush, nil
}

View File

@ -0,0 +1,37 @@
# Exemple de déploiement multi-noeuds avec Docker-Compose
Le répertoire [`misc/docker-compose`](./) contient un exemple de déploiement de Bouncer multi-noeuds avec:
- 3 instances du service `bouncer-proxy`;
- 1 instance du service `haproxy` en frontal en charge du load-balancing;
- 1 instance du service `bouncer-admin`;
- 1 serveur Redis.
## Prérequis
- [Docker Compose](https://docs.docker.com/compose/)
## Étapes
1. Se positionner dans le répertoire puis lancer l'environnement avec la commande `docker-compose`:
```bash
cd misc/docker-compose
docker-compose up
```
2. Entrer dans le conteneur `bouncer-admin` puis créer un jeton d'accès:
```bash
docker-compose exec bouncer-admin /bin/sh
bouncer auth create-token --role writer > .bouncer-token
```
3. Créer un proxy via le CLI:
```bash
bouncer admin proxy create --proxy-name myproxy --proxy-to "https://www.cadoles.com/"
bouncer admin proxy update --proxy-name myproxy --proxy-enabled=true
```
4. Via votre navigateur, accéder à l'URL http://127.0.0.1:8080. La page du site Cadoles devrait s'afficher. Dans le log de la commande `docker-compose up` vous devriez voir que les requêtes sont routées à tour de rôle sur les 3 instances de Bouncer en exécution.

View File

@ -0,0 +1 @@
{"d":"JuBw5OsGv3rPgVczxUgtJ6iUQ41LQu4Xpu-t8IKI_z8r-BZBlbndxidPmRlGZASLGL3rhY4qw6_ScFxakrMpCreO1RMU0kqtz--N48BXFnW5tEgr1voyyKP__bPssQNn6PgkoyAd11es7MEKlBff_DtGrcSkVRgU0zDZB-vIU0aNEIZPNw0icbYqc1u_QQNPpBU9cw6P33WHhzvfCVAkZKRszwznhiPM08n1vjpiA7e1kQ8a6OC4IFZBvohkmpmyOq1g1OLRABQ83YPCjGjCAejO-jEWkbLksp6rAl_YYpCvfBAjFV76JuZq4eh5IU82LsSfi3PGYBkhxWuLY779XQ","dp":"gljHOQowGK7fVn2DJizWtgRIDJuKpKnoX2PWNJUbm2WZwcEPZalAkxn7Y-w_reLVJZuRpfKEUMS-Tn3-CwI1ZjCHPqMPTXcoG0Pe2E-Z88jOs9lW4XSOASiiM980VIvkV1xCxDJkN3NsDFQ9j9kRGnKuMnsucCW3AKaU917hXNU","dq":"mqY19JcEBDnzS70_XkAsOKqPzemOScax66b-4N6zrsgeLVlRjHffY9uCAgBWzlxOidRdQN8q23ZJB4fqsKB2w00Iw7Jxx94IoAKGjKDT5iB48Y_kdKLAwSHRTXsqA9GG3po_H_JpP_EqX4TDBYtqQZuBD_tACP9HbLYMi_V2YU8","e":"AQAB","kty":"RSA","n":"sam0X0BGcuFwX8z3Wde8cv2o_zl6A9ghpkT0tCjw8qH3GNWrbAqzncSWdHBzoChBgAbuTOVs-ixYC0KeUhwFdc8Ul-jmKJWFaS8kIr3y4EH62-vLgMuIKfaxbsyUG6KMkJfnftge1jPO4ccddNej9msxcqTxu37dcgstutwtd6QkS9p5RrNbDBc8-Z7SQ4TuxJfP8msXRnCPJ-I44yszGdQF1Np2DXakJHVn8PBrDh3iSFwORw8jxNS4oS0OlBl5aSc0t5XkkaNcSU2a50SKts290w54fl6MPJ1sLnnznLy4uu37-nrfEUvqRLDZL9B1F82RM1dtLIIiN4gnSrMlpQ","p":"wOmFPhAT_wXWzMuwtEdYIer3-CiOWxFKpFL09eEJkJ29MIUchEaoiJaUAghqPxM48llfOVaUaLbFVxmo5U3fyjNMaP-nHMUBwojutykMK-gC2R3J4bQgFWfKbGSL7M7UsextAvpq9iiOuR0LNE-xTfCgPIxHVdPZskO3yx0DkjM","q":"68OGRb0tLRjb_PpkGctcSjEz_vvcyjzxGL-fn4_h4GCw98Xrj6Y4rZ4lfWWRSeDohSvdd-ICSlxvxkQOIOcA0H7jyJcBC0KDs4hX5BRGJNDri3QX0ry4_F1ptAdbfiFgQGqCfMRCr7L60Tfd_6tLczvny7eEBKQNGdj6dLfhgMc","qi":"DFwixyxUDf0REPLLa8hOKieRL95_AH9rbYWzStBOdSjKWra5l0reD6a4bbvAYvl0e8qCcRI6S8Nzpz0BYm4sJL7poVOnjxqvBY3Q9Ppf4Mq8lW39pOCJcqOHIvvYHsMjTC5uwp7Yg2p0GvxuUibbyNL1PXf6WZ_szVP_oSMrCXA"}

View File

@ -0,0 +1,47 @@
admin:
http:
host: 127.0.0.1
port: 8081
cors:
allowedOrigins:
- http://localhost:3001
allowCredentials: true
allowMethods:
- POST
- GET
- PUT
- DELETE
allowedHeaders:
- Origin
- Accept
- Content-Type
- Authorization
- Sentry-Trace
debug: false
auth:
issuer: http://127.0.0.1:8081
privateKey: /etc/bouncer/admin-key.json
metrics:
enabled: true
endpoint: /.bouncer/metrics
basicAuth: null
proxy:
http:
host: 0.0.0.0
port: 8080
metrics:
enabled: true
endpoint: /.bouncer/metrics
basicAuth: null
redis:
addresses:
- redis:6379
master: ""
logger:
level: 1
format: human
layers:
queue:
templateDir: /usr/share/bouncer/layers/queue/templates
defaultKeepAlive: 1m0s

View File

@ -0,0 +1,42 @@
version: "2"
services:
haproxy:
image: reg.cadoles.com/proxy_cache/library/haproxy:2.7-alpine
ports:
- 8080:8080
links:
- bouncer-proxy-1
- bouncer-proxy-2
- bouncer-proxy-3
volumes:
- ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
bouncer-admin:
image: reg.cadoles.com/cadoles/bouncer:latest
command: bouncer -c /etc/bouncer/config.yml server admin run
links:
- redis
volumes:
- ./bouncer/config.yml:/etc/bouncer/config.yml
- ./bouncer/admin-key.json:/etc/bouncer/admin-key.json
bouncer-proxy-1: &bouncer-proxy
image: reg.cadoles.com/cadoles/bouncer:latest
command: bouncer -c /etc/bouncer/config.yml server proxy run
links:
- redis
volumes:
- ./bouncer/config.yml:/etc/bouncer/config.yml
- ./bouncer/admin-key.json:/etc/bouncer/admin-key.json
bouncer-proxy-2: *bouncer-proxy
bouncer-proxy-3: *bouncer-proxy
redis:
image: reg.cadoles.com/proxy_cache/library/redis:7-alpine
command: redis-server --save 60 1 --loglevel verbose
volumes:
- redis-data:/data
volumes:
redis-data:

View File

@ -0,0 +1,21 @@
global
maxconn 100
log stdout format raw local0 info
defaults
mode http
timeout client 30s
timeout server 30s
timeout connect 30s
log global
option httplog
frontend proxy
bind *:8080
default_backend bouncer-proxy
backend bouncer-proxy
mode http
server bouncer-proxy-1 bouncer-proxy-1:8080 check
server bouncer-proxy-2 bouncer-proxy-2:8080 check
server bouncer-proxy-3 bouncer-proxy-3:8080 check

View File

@ -26,10 +26,45 @@ admin:
- Authorization
- Sentry-Trace
debug: false
# Authentification JWT
auth:
# Origine du jeton JWT
issuer: http://127.0.0.1:8081
# Clé privée permettant de signer les jetons
# JWT générés pour l'usage de l'API d'administration.
privateKey: /etc/bouncer/admin-key.json
# Métriques Prometheus
metrics:
# Activer ou désactiver la publication des métriques
enabled: true
# Route de publication des métriques
endpoint: /.bouncer/metrics
# Authentification "basic auth" sur la page
# de publication
# Mettre à null pour désactiver l'authentification
basicAuth: null
# Configuration de l'intégration Sentry
# Voir https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions
sentry:
dsn: ""
debug: false
flushTimeout: 2s
attachStacktrace: true
sampleRate: 1
enableTracing: true
tracesSampleRate: 0.2
profilesSampleRate: 1
ignoreErrors: []
sendDefaultPII: false
serverName: ""
environment: ""
maxBreadcrumbs: 0
maxSpans: 1000
maxErrorDepth: 10
# Configuration du service "proxy"
proxy:
http:
@ -38,6 +73,63 @@ proxy:
host: 0.0.0.0
# Port d'écoute du service
port: 8080
# Métriques Prometheus
metrics:
# Activer ou désactiver la publication des métriques
enabled: true
# Route de publication des métriques
endpoint: /.bouncer/metrics
# Authentification "basic auth" sur la page
# de publication
# Mettre à null pour désactiver l'authentification
basicAuth:
credentials:
prom: etheus
# Configuration du transport HTTP(S)
# Voir https://pkg.go.dev/net/http#Transport
transport:
forceAttemptHTTP2: true
maxIdleConns: 100
maxIdleConnsPerHost: 100
maxConnsPerHost: 100
idleConnTimeout: 1m30s
tlsHandshakeTimeout: 10s
expectContinueTimeout: 1s
disableKeepAlives: false
disableCompression: false
responseHeaderTimeout: 10s
writeBufferSize: 4096
readBufferSize: 4096
maxResponseHeaderBytes: 0
# Configuration de l'intégration Sentry
# Voir https://pkg.go.dev/github.com/getsentry/sentry-go?utm_source=godoc#ClientOptions
sentry:
dsn: ""
debug: false
flushTimeout: 2s
attachStacktrace: true
sampleRate: 1
enableTracing: true
tracesSampleRate: 0.2
profilesSampleRate: 1
ignoreErrors: []
sendDefaultPII: false
serverName: ""
environment: ""
maxBreadcrumbs: 0
maxSpans: 1000
maxErrorDepth: 10
# Configuration des connexions TCP
# Voir https://pkg.go.dev/net#Dialer
dial:
timeout: 30s
keepAlive: 30s
fallbackDelay: 300ms
dualStack: true
# Configuration du client Redis
#