diff --git a/.gitignore b/.gitignore index 607f345..219ca77 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ /admin-key.json /.bouncer-token /data -/out \ No newline at end of file +/out +.dockerconfigjson diff --git a/.goreleaser.yaml b/.goreleaser.yaml index fab9a1c..ed9257a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,37 +1,37 @@ project_name: bouncer before: hooks: - - go mod tidy - - go generate ./... + - go mod tidy + - go generate ./... builds: - - id: bouncer - env: - - CGO_ENABLED=0 - ldflags: - - -s - - -w - - -X 'main.GitRef={{ .Commit }}' - - -X 'main.ProjectVersion={{ .Version }}' - - -X 'main.BuildDate={{ .Date }}' - - -X 'main.DefaultConfigPath=/etc/bouncer/config.yml' - gcflags: - - -trimpath="${PWD}" - asmflags: - - -trimpath="${PWD}" - goos: - - linux - goarch: - - amd64 - - arm64 - - "386" - main: ./cmd/bouncer +- id: bouncer + env: + - CGO_ENABLED=0 + ldflags: + - -s + - -w + - -X 'main.GitRef={{ .Commit }}' + - -X 'main.ProjectVersion={{ .Version }}' + - -X 'main.BuildDate={{ .Date }}' + - -X 'main.DefaultConfigPath=/etc/bouncer/config.yml' + gcflags: + - -trimpath="${PWD}" + asmflags: + - -trimpath="${PWD}" + goos: + - linux + goarch: + - amd64 + - arm64 + - "386" + main: ./cmd/bouncer archives: - - id: bouncer - builds: ["bouncer"] - name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}' - files: - - README.md - - misc/packaging/common/config.yml +- id: bouncer + builds: ["bouncer"] + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}' + files: + - README.md + - misc/packaging/common/config.yml checksum: name_template: 'checksums.txt' snapshot: @@ -40,100 +40,109 @@ changelog: sort: asc filters: exclude: - - '^docs:' - - '^test:' + - '^docs:' + - '^test:' nfpms: - - id: bouncer-bin - builds: - - "bouncer" - package_name: bouncer-bin - homepage: https://forge.cadoles.com/Cadoles/bouncer - maintainer: Cadoles - description: |- - reverse proxy server with dynamic queuing management - binaries - license: AGPL-3.0 - formats: - - apk - - deb - - rpm - contents: - - src: misc/packaging/common/config.yml - dst: /etc/bouncer/config.yml - type: config - - src: layers - dst: /etc/bouncer/layers - type: config - - id: bouncer-admin - meta: true - package_name: bouncer-admin - homepage: https://forge.cadoles.com/Cadoles/bouncer - maintainer: Cadoles - dependencies: - - bouncer-bin - description: |- - reverse proxy server with dynamic queuing management - administration service - license: AGPL-3.0 - formats: - - apk - - deb - - rpm - contents: - - src: misc/packaging/systemd/bouncer-admin.systemd.service - dst: /usr/lib/systemd/system/bouncer-admin.service - packager: deb - - src: misc/packaging/systemd/bouncer-admin.systemd.service - dst: /usr/lib/systemd/system/bouncer-admin.service - packager: rpm - - src: misc/packaging/openrc/bouncer-admin.openrc.sh - dst: /etc/init.d/bouncer-admin - file_info: - mode: 0755 - packager: apk - - dst: /usr/share/bouncer - type: dir - file_info: - mode: 0700 - - dst: /var/log/bouncer - type: dir - file_info: - mode: 0700 - packager: apk - scripts: - postinstall: "misc/packaging/common/postinstall-bouncer-admin.sh" - - id: bouncer-proxy - meta: true - dependencies: - - bouncer-bin - package_name: bouncer-proxy - homepage: https://forge.cadoles.com/Cadoles/bouncer - maintainer: Cadoles - description: |- - reverse proxy server with dynamic queuing management - proxy service - license: AGPL-3.0 - formats: - - apk - - deb - - rpm - contents: - - src: misc/packaging/systemd/bouncer-proxy.systemd.service - dst: /usr/lib/systemd/system/bouncer-proxy.service - packager: deb - - src: misc/packaging/systemd/bouncer-proxy.systemd.service - dst: /usr/lib/systemd/system/bouncer-proxy.service - packager: rpm - - src: misc/packaging/openrc/bouncer-proxy.openrc.sh - dst: /etc/init.d/bouncer-proxy - file_info: - mode: 0755 - packager: apk - - dst: /usr/share/bouncer - type: dir - file_info: - mode: 0700 - - dst: /var/log/bouncer - type: dir - file_info: - mode: 0700 - packager: apk - scripts: - postinstall: "misc/packaging/common/postinstall-bouncer-proxy.sh" +- id: bouncer-bin + builds: + - "bouncer" + package_name: bouncer-bin + homepage: https://forge.cadoles.com/Cadoles/bouncer + maintainer: Cadoles + description: |- + reverse proxy server with dynamic queuing management - binaries + license: AGPL-3.0 + formats: + - apk + - deb + - rpm + - archlinux + contents: + - src: misc/packaging/common/config.yml + dst: /etc/bouncer/config.yml + type: config + - src: layers + dst: /etc/bouncer/layers + type: config +- id: bouncer-admin + meta: true + package_name: bouncer-admin + homepage: https://forge.cadoles.com/Cadoles/bouncer + maintainer: Cadoles + dependencies: + - bouncer-bin + description: |- + reverse proxy server with dynamic queuing management - administration service + license: AGPL-3.0 + formats: + - apk + - deb + - rpm + - archlinux + contents: + - src: misc/packaging/systemd/bouncer-admin.systemd.service + dst: /usr/lib/systemd/system/bouncer-admin.service + packager: deb + - src: misc/packaging/systemd/bouncer-admin.systemd.service + dst: /usr/lib/systemd/system/bouncer-admin.service + packager: rpm + - src: misc/packaging/systemd/bouncer-admin.systemd.service + dst: /usr/lib/systemd/system/bouncer-admin.service + packager: archlinux + - src: misc/packaging/openrc/bouncer-admin.openrc.sh + dst: /etc/init.d/bouncer-admin + file_info: + mode: 0755 + packager: apk + - dst: /usr/share/bouncer + type: dir + file_info: + mode: 0700 + - dst: /var/log/bouncer + type: dir + file_info: + mode: 0700 + packager: apk + scripts: + postinstall: "misc/packaging/common/postinstall-bouncer-admin.sh" +- id: bouncer-proxy + meta: true + dependencies: + - bouncer-bin + package_name: bouncer-proxy + homepage: https://forge.cadoles.com/Cadoles/bouncer + maintainer: Cadoles + description: |- + reverse proxy server with dynamic queuing management - proxy service + license: AGPL-3.0 + formats: + - apk + - deb + - rpm + - archlinux + contents: + - src: misc/packaging/systemd/bouncer-proxy.systemd.service + dst: /usr/lib/systemd/system/bouncer-proxy.service + packager: deb + - src: misc/packaging/systemd/bouncer-proxy.systemd.service + dst: /usr/lib/systemd/system/bouncer-proxy.service + packager: rpm + - src: misc/packaging/systemd/bouncer-proxy.systemd.service + dst: /usr/lib/systemd/system/bouncer-proxy.service + packager: archlinux + - src: misc/packaging/openrc/bouncer-proxy.openrc.sh + dst: /etc/init.d/bouncer-proxy + file_info: + mode: 0755 + packager: apk + - dst: /usr/share/bouncer + type: dir + file_info: + mode: 0700 + - dst: /var/log/bouncer + type: dir + file_info: + mode: 0700 + packager: apk + scripts: + postinstall: "misc/packaging/common/postinstall-bouncer-proxy.sh" diff --git a/Makefile b/Makefile index 13ec1aa..4d62fb2 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,9 @@ GOTEST_ARGS ?= -short OPENWRT_DEVICE ?= 192.168.1.1 +SIEGE_URLS_FILE ?= misc/siege/urls.txt +SIEGE_CONCURRENCY ?= 100 + watch: tools/modd/bin/modd deps ## Watching updated files - live reload ( set -o allexport && source .env && set +o allexport && tools/modd/bin/modd ) @@ -105,7 +108,10 @@ grafterm: tools/grafterm/bin/grafterm tools/grafterm/bin/grafterm -c ./misc/grafterm/dashboard.json -v job=bouncer-proxy -r 5s siege: - siege -i -c 100 -f ./misc/siege/urls.txt + $(eval TMP := $(shell mktemp)) + cat $(SIEGE_URLS_FILE) | envsubst > $(TMP) + siege -i -b -c $(SIEGE_CONCURRENCY) -f $(TMP) + rm -rf $(TMP) tools/gitea-release/bin/gitea-release.sh: mkdir -p tools/gitea-release/bin diff --git a/doc/fr/getting-started.md b/doc/fr/getting-started.md index db675e0..7591b99 100644 --- a/doc/fr/getting-started.md +++ b/doc/fr/getting-started.md @@ -41,7 +41,7 @@ 5. Tester que le CLI est en capacité d'interroger l'API d'administration ```bash - bouncer admin query proxy + bouncer admin proxy query ``` Un message équivalent à celui ci devrait s'afficher: @@ -92,4 +92,4 @@ 3. Ouvrir la page `https://:8080/` dans un navigateur. Le site Cadoles s'affiche ! -**Bravo, vous avez créé votre premier proxy avec Bouncer !** \ No newline at end of file +**Bravo, vous avez créé votre premier proxy avec Bouncer !** diff --git a/internal/config/redis.go b/internal/config/redis.go index 61faefd..9fbaddc 100644 --- a/internal/config/redis.go +++ b/internal/config/redis.go @@ -1,5 +1,7 @@ package config +import "time" + const ( RedisModeSimple = "simple" RedisModeSentinel = "sentinel" @@ -7,13 +9,19 @@ const ( ) type RedisConfig struct { - Adresses InterpolatedStringSlice `yaml:"addresses"` - Master InterpolatedString `yaml:"master"` + Adresses InterpolatedStringSlice `yaml:"addresses"` + Master InterpolatedString `yaml:"master"` + ReadTimeout InterpolatedDuration `yaml:"readTimeout"` + WriteTimeout InterpolatedDuration `yaml:"writeTimeout"` + DialTimeout InterpolatedDuration `yaml:"dialTimeout"` } func NewDefaultRedisConfig() RedisConfig { return RedisConfig{ - Adresses: InterpolatedStringSlice{"localhost:6379"}, - Master: "", + Adresses: InterpolatedStringSlice{"localhost:6379"}, + Master: "", + ReadTimeout: InterpolatedDuration(30 * time.Second), + WriteTimeout: InterpolatedDuration(30 * time.Second), + DialTimeout: InterpolatedDuration(30 * time.Second), } } diff --git a/internal/jwk/jwk.go b/internal/jwk/jwk.go index afa0bdf..727b2dd 100644 --- a/internal/jwk/jwk.go +++ b/internal/jwk/jwk.go @@ -4,7 +4,6 @@ import ( "crypto/rand" "crypto/rsa" "encoding/json" - "io/ioutil" "os" "github.com/btcsuite/btcd/btcutil/base58" @@ -56,7 +55,7 @@ func PublicKeySet(keys ...jwk.Key) (jwk.Set, error) { } func LoadOrGenerate(path string, size int) (jwk.Key, error) { - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil && !errors.Is(err, os.ErrNotExist) { return nil, errors.WithStack(err) } @@ -72,7 +71,7 @@ func LoadOrGenerate(path string, size int) (jwk.Key, error) { return nil, errors.WithStack(err) } - if err := ioutil.WriteFile(path, data, 0o640); err != nil { + if err := os.WriteFile(path, data, 0o640); err != nil { return nil, errors.WithStack(err) } } diff --git a/internal/setup/proxy_repository.go b/internal/setup/proxy_repository.go index 3ebc6f5..67b777b 100644 --- a/internal/setup/proxy_repository.go +++ b/internal/setup/proxy_repository.go @@ -10,11 +10,7 @@ import ( ) func NewProxyRepository(ctx context.Context, conf config.RedisConfig) (store.ProxyRepository, error) { - rdb := redis.NewUniversalClient(&redis.UniversalOptions{ - Addrs: conf.Adresses, - MasterName: string(conf.Master), - }) - + rdb := newRedisClient(conf) return redisStore.NewProxyRepository(rdb), nil } diff --git a/internal/setup/queue_layer.go b/internal/setup/queue_layer.go index 15ed188..8b97f70 100644 --- a/internal/setup/queue_layer.go +++ b/internal/setup/queue_layer.go @@ -8,7 +8,6 @@ import ( "forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue" queueRedis "forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue/redis" "github.com/pkg/errors" - "github.com/redis/go-redis/v9" ) func init() { @@ -36,10 +35,6 @@ func setupQueueLayer(conf *config.Config) (director.Layer, error) { } func newQueueAdapter(redisConf config.RedisConfig) (queue.Adapter, error) { - rdb := redis.NewUniversalClient(&redis.UniversalOptions{ - Addrs: redisConf.Adresses, - MasterName: string(redisConf.Master), - }) - + rdb := newRedisClient(redisConf) return queueRedis.NewAdapter(rdb, 2), nil } diff --git a/internal/setup/redis.go b/internal/setup/redis.go new file mode 100644 index 0000000..3ec583a --- /dev/null +++ b/internal/setup/redis.go @@ -0,0 +1,20 @@ +package setup + +import ( + "time" + + "forge.cadoles.com/cadoles/bouncer/internal/config" + "github.com/redis/go-redis/v9" +) + +func newRedisClient(conf config.RedisConfig) redis.UniversalClient { + return redis.NewUniversalClient(&redis.UniversalOptions{ + Addrs: conf.Adresses, + MasterName: string(conf.Master), + ReadTimeout: time.Duration(conf.ReadTimeout), + WriteTimeout: time.Duration(conf.WriteTimeout), + DialTimeout: time.Duration(conf.DialTimeout), + RouteByLatency: true, + ContextTimeoutEnabled: true, + }) +} diff --git a/misc/images/bouncer/Dockerfile b/misc/images/bouncer/Dockerfile new file mode 100644 index 0000000..9e8607d --- /dev/null +++ b/misc/images/bouncer/Dockerfile @@ -0,0 +1,49 @@ +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 + +# 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 + +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/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 + +EXPOSE 8080 +EXPOSE 8081 + +ENV BOUNCER_CONFIG=/etc/bouncer/config.yml + +CMD ["bouncer"] \ No newline at end of file diff --git a/misc/k8s/README.md b/misc/k8s/README.md new file mode 100644 index 0000000..f18d43e --- /dev/null +++ b/misc/k8s/README.md @@ -0,0 +1,66 @@ +# Kubernetes + +## Initialize your project + +1. Generate the Docker configuration to enable image builds with Kaniko and communicate with reg.cadoles.com + + ```shell + docker login reg.cadoles.com + mkdir -p misc/k8s/kustomization/base/secrets/dockerconfig + docker --config misc/k8s/kustomization/base/secrets/dockerconfig login reg.cadoles.com + mv misc/k8s/kustomization/base/secrets/dockerconfig/config.json misc/k8s/kustomization/base/secrets/dockerconfig/.dockerconfigjson + mkdir -p misc/k8s/kustomization/overlays/dev/secrets/dockerconfig + cp misc/k8s/kustomization/base/secrets/dockerconfig/.dockerconfigjson misc/k8s/kustomization/overlays/dev/secrets/dockerconfig/.dockerconfigjson + ``` + +## Getting started with Kind + +1. Create your [Kind](https://kind.sigs.k8s.io/) cluster + + ```shell + kind create cluster --config misc/k8s/kind/bouncer-cluster.yaml + ``` + +2. Deploy required operators + + ```shell + kubectl apply -k misc/k8s/kind/cluster --server-side + ``` + +3. Deploy your Bouncer development environment + + ```shell + skaffold dev -p dev --cleanup=false --default-repo reg.cadoles.com/ + ``` + +## Testing + +1. Open shell in bouncer-admin pod + + ```shell + kubectl exec -it -n bouncer-dev bouncer-admin- -- /bin/sh + ``` + +2. Create an authentication token + + ```shell + bouncer --config /etc/bouncer/config.yml auth create-token --role writer --subject $(whoami) > .bouncer-token + ``` + +3. Create a proxy and enable it + + ```shell + bouncer admin proxy create --proxy-to https://www.cadoles.com --proxy-name cadoles + bouncer admin proxy update --proxy-name cadoles --proxy-enabled=true + ``` + +4. With you host web browser, open http://localhost:9000, you should see the Cadoles website. + +## Benchmarking + +You can use [`siege`](https://github.com/JoeDog/siege) to benchmark your instance with the Cadoles proxy. + +```shell +BASE_URL=http://localhost:9000 make siege +``` + diff --git a/misc/k8s/kind/bouncer-cluster.yaml b/misc/k8s/kind/bouncer-cluster.yaml new file mode 100644 index 0000000..6d991fd --- /dev/null +++ b/misc/k8s/kind/bouncer-cluster.yaml @@ -0,0 +1,3 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +name: bouncer-dev diff --git a/misc/k8s/kind/cluster/kustomization.yaml b/misc/k8s/kind/cluster/kustomization.yaml new file mode 100644 index 0000000..5f43460 --- /dev/null +++ b/misc/k8s/kind/cluster/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- https://forge.cadoles.com/CadolesKube/c-kustom//base/redis?ref=develop \ No newline at end of file diff --git a/misc/k8s/kustomization/base/kustomization.yaml b/misc/k8s/kustomization/base/kustomization.yaml new file mode 100644 index 0000000..9bb8fed --- /dev/null +++ b/misc/k8s/kustomization/base/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: bouncer + +resources: +- ./resources/namespace.yaml +- ./resources/bouncer-server +- ./resources/bouncer-admin +- ./resources/redis + diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/files/admin-key.json b/misc/k8s/kustomization/base/resources/bouncer-admin/files/admin-key.json new file mode 100644 index 0000000..59025df --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/files/admin-key.json @@ -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"} \ No newline at end of file diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml b/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml new file mode 100644 index 0000000..c38d4a3 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/files/config.yml @@ -0,0 +1,36 @@ +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 + +redis: + addresses: + - rfs-bouncer-redis:${RFS_BOUNCER_REDIS_SERVICE_PORT} + master: mymaster + +logger: + level: 2 + format: human diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml new file mode 100644 index 0000000..ec6818d --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/kustomization.yaml @@ -0,0 +1,12 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ./resources/service.yaml +- ./resources/deployment.yaml + +configMapGenerator: +- name: bouncer-admin-config + files: + - ./files/config.yml + - ./files/admin-key.json \ No newline at end of file diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml new file mode 100644 index 0000000..21216da --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bouncer-admin + labels: + app: bouncer-admin + io.kompose.service: bouncer-admin +spec: + replicas: 3 + selector: + matchLabels: + app: bouncer-admin + template: + metadata: + labels: + app: bouncer-admin + io.kompose.service: bouncer-admin + spec: + containers: + - name: bouncer-admin + image: reg.cadoles.com/cadoles/bouncer:v2024.2.5-1602626 + command: ["bouncer", "--debug", "-c", "/etc/bouncer/config.yml", "server", "admin", "run"] + imagePullPolicy: Always + resources: {} + ports: + - name: bouncer-admin + containerPort: 8081 + volumeMounts: + - mountPath: /etc/bouncer/ + name: bouncer-admin-config + volumes: + - name: bouncer-admin-config + configMap: + name: bouncer-admin-config \ No newline at end of file diff --git a/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml new file mode 100644 index 0000000..258ac42 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-admin/resources/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: bouncer-admin + name: bouncer-admin +spec: + type: ClusterIP + ports: + - name: bouncer-admin + port: 8081 + targetPort: bouncer-admin + selector: + io.kompose.service: bouncer-admin diff --git a/misc/k8s/kustomization/base/resources/bouncer-server/files/config.yml b/misc/k8s/kustomization/base/resources/bouncer-server/files/config.yml new file mode 100644 index 0000000..1a9de19 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-server/files/config.yml @@ -0,0 +1,22 @@ +proxy: + http: + host: 0.0.0.0 + port: 8080 + metrics: + enabled: true + endpoint: /.bouncer/metrics + basicAuth: null + +layers: + queue: + templateDir: /usr/share/bouncer/layers/queue/templates + defaultKeepAlive: 1m0s + +redis: + addresses: + - rfs-bouncer-redis:${RFS_BOUNCER_REDIS_SERVICE_PORT} + master: mymaster + +logger: + level: 2 + format: human diff --git a/misc/k8s/kustomization/base/resources/bouncer-server/kustomization.yaml b/misc/k8s/kustomization/base/resources/bouncer-server/kustomization.yaml new file mode 100644 index 0000000..59d9407 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-server/kustomization.yaml @@ -0,0 +1,11 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ./resources/service.yaml +- ./resources/deployment.yaml + +configMapGenerator: +- name: bouncer-server-config + files: + - ./files/config.yml diff --git a/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml b/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml new file mode 100644 index 0000000..a7c3abc --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-server/resources/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bouncer-server + labels: + app: bouncer-server + io.kompose.service: bouncer-server +spec: + replicas: 3 + selector: + matchLabels: + app: bouncer-server + template: + metadata: + labels: + app: bouncer-server + io.kompose.service: bouncer-server + spec: + containers: + - name: bouncer-server + image: reg.cadoles.com/cadoles/bouncer:v2024.2.5-1602626 + command: ["bouncer", "-c", "/etc/bouncer/config.yml", "server", "proxy", "run"] + imagePullPolicy: Always + resources: {} + ports: + - name: bouncer-server + containerPort: 8080 + volumeMounts: + - mountPath: /etc/bouncer/ + name: bouncer-server-config + volumes: + - name: bouncer-server-config + configMap: + name: bouncer-server-config diff --git a/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml b/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml new file mode 100644 index 0000000..16e1ab3 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/bouncer-server/resources/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: bouncer-server + name: bouncer-server +spec: + type: ClusterIP + ports: + - name: bouncer-server + port: 8080 + targetPort: bouncer-server + selector: + io.kompose.service: bouncer-server diff --git a/misc/k8s/kustomization/base/resources/namespace.yaml b/misc/k8s/kustomization/base/resources/namespace.yaml new file mode 100644 index 0000000..bd46fb4 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: bouncer diff --git a/misc/k8s/kustomization/base/resources/redis/kustomization.yaml b/misc/k8s/kustomization/base/resources/redis/kustomization.yaml new file mode 100644 index 0000000..9efdd93 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/redis/kustomization.yaml @@ -0,0 +1,15 @@ + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ./resources/redis-cluster.yaml + +vars: +- name: REDIS_SERVICE_NAME + objref: + name: bouncer-redis + apiVersion: databases.spotahome.com/v1 + kind: RedisFailover + fieldref: + fieldpath: metadata.name diff --git a/misc/k8s/kustomization/base/resources/redis/resources/redis-cluster.yaml b/misc/k8s/kustomization/base/resources/redis/resources/redis-cluster.yaml new file mode 100644 index 0000000..d647ee2 --- /dev/null +++ b/misc/k8s/kustomization/base/resources/redis/resources/redis-cluster.yaml @@ -0,0 +1,21 @@ +apiVersion: databases.spotahome.com/v1 +kind: RedisFailover +metadata: + name: bouncer-redis +spec: + sentinel: + replicas: 3 + resources: + requests: + cpu: 100m + limits: + memory: 100Mi + redis: + replicas: 3 + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 400m + memory: 500Mi diff --git a/misc/k8s/kustomization/overlays/dev/kustomization.yaml b/misc/k8s/kustomization/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..d692dc9 --- /dev/null +++ b/misc/k8s/kustomization/overlays/dev/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: bouncer-dev + +resources: +- ../../base + +secretGenerator: +- files: + - secrets/dockerconfig/.dockerconfigjson + name: regcred-dev + type: kubernetes.io/dockerconfigjson + +patches: +- path: patches/add-registry-pull-secret.patch.yaml + target: + kind: Deployment + version: v1 diff --git a/misc/k8s/kustomization/overlays/dev/patches/add-registry-pull-secret.patch.yaml b/misc/k8s/kustomization/overlays/dev/patches/add-registry-pull-secret.patch.yaml new file mode 100644 index 0000000..1e4b8d2 --- /dev/null +++ b/misc/k8s/kustomization/overlays/dev/patches/add-registry-pull-secret.patch.yaml @@ -0,0 +1,4 @@ +- op: add + path: "/spec/template/spec/imagePullSecrets" + value: + - name: regcred-dev diff --git a/misc/packaging/common/config.yml b/misc/packaging/common/config.yml index d7d53a2..31e41cb 100644 --- a/misc/packaging/common/config.yml +++ b/misc/packaging/common/config.yml @@ -1,182 +1,179 @@ # Configuration du service "admin" admin: - http: - # Hôte d'écoute du service, - # 0.0.0.0 pour écouter sur toutes les interfaces - host: 127.0.0.1 - # Port d'écoute du service - port: 8081 - # Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For - # pour le calcul de l'adresse distante à l'origine des requêtes - useRealIP: true + http: + # Hôte d'écoute du service, + # 0.0.0.0 pour écouter sur toutes les interfaces + host: 127.0.0.1 + # Port d'écoute du service + port: 8081 + # Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For + # pour le calcul de l'adresse distante à l'origine des requêtes + useRealIP: true - # Configuration CORS du service - # Uniquement nécessaire si un frontend web - # est branché sur l'API d'administration. - cors: - allowedOrigins: - - http://localhost:8081 - allowCredentials: true - allowMethods: - - POST - - GET - - PUT - - DELETE - allowedHeaders: - - Origin - - Accept - - Content-Type - - 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 + # Configuration CORS du service + # Uniquement nécessaire si un frontend web + # est branché sur l'API d'administration. + cors: + allowedOrigins: + - http://localhost:8081 + allowCredentials: true + allowMethods: + - POST + - GET + - PUT + - DELETE + allowedHeaders: + - Origin + - Accept + - Content-Type + - Authorization + - Sentry-Trace + debug: false - # 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 - # Les couples d'identifiants doivent être spécifiés sous la forme: - # basicAuth: - # credentials: - # : - 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 + # 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: - # Hôte d'écoute du service, - # 0.0.0.0 pour écouter sur toutes les interfaces - host: 0.0.0.0 - # Port d'écoute du service - port: 8080 - # Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For - # pour le calcul de l'adresse distante à l'origine des requêtes - useRealIP: true - - # 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: - # Les couples d'identifiants doivent être spécifiés - # sous la forme ": " - credentials: - prometheus: changeme + http: + # Hôte d'écoute du service, + # 0.0.0.0 pour écouter sur toutes les interfaces + host: 0.0.0.0 + # Port d'écoute du service + port: 8080 + # Utiliser les entêtes HTTP True-Client-IP, X-Real-IP ou X-Forwarded-For + # pour le calcul de l'adresse distante à l'origine des requêtes + useRealIP: true - # 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 + # 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 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 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 des connexions TCP - # Voir https://pkg.go.dev/net#Dialer - dial: - timeout: 30s - keepAlive: 30s - fallbackDelay: 300ms - dualStack: true + # 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 # # Les modes "standalone", "sentinel" et "cluster" de Redis sont supportés: # - Mode "standalone": renseigner une seule entrée dans redis.addresses; -# - Mode "sentinel": renseigner une adresse dans redis.master et une ou plusieurs adresses dans redis.addresses; +# - Mode "sentinel": renseigner le nom du master sentinel dans redis.master et une ou plusieurs adresses dans redis.addresses; # - Mode "cluster": renseigner plusieurs adresses dans redis.addresses et laisser redis.master vide. redis: - addresses: - - localhost:6379 - master: "" + addresses: + - localhost:6379 + master: "" + writeTimeout: 30s + readTimeout: 30s + dialTimeout: 30s # Configuration des logs logger: - # Niveau de verbosité - # 0 - DEBUG - # 1 - INFO - # 2 - WARNING - # 3 - ERROR - # 4 - FATAL - level: 1 - # Format des logs, "human" ou "json" - format: human + # Niveau de verbosité + # 0 - DEBUG + # 1 - INFO + # 2 - WARNING + # 3 - ERROR + # 4 - FATAL + level: 2 + # Format des logs, "human" ou "json" + format: human # Configuration des différents layers layers: - # Configuration du layer "queue" - queue: - # Répertoire contenant les templates - templateDir: "/etc/bouncer/layers/queue/templates" - # Temps de vie par défaut d'une session - defaultKeepAlive: 1m - - # Configuration du layer "circuitbreaker" - circuitbreaker: - # Répertoire contenant les templates - templateDir: "/etc/bouncer/layers/circuitbreaker/templates" - \ No newline at end of file + # Configuration du layer "queue" + queue: + # Répertoire contenant les templates + templateDir: "/etc/bouncer/layers/queue/templates" + # Temps de vie par défaut d'une session + defaultKeepAlive: 1m + + # Configuration du layer "circuitbreaker" + circuitbreaker: + # Répertoire contenant les templates + templateDir: "/etc/bouncer/layers/circuitbreaker/templates" + diff --git a/misc/siege/urls.txt b/misc/siege/urls.txt index a830156..6d7cf9b 100644 --- a/misc/siege/urls.txt +++ b/misc/siege/urls.txt @@ -1,6 +1,6 @@ -http://localhost:8080/blog/ -http://localhost:8080/services/ -http://localhost:8080/ -http://localhost:8080/recrutement/ -http://localhost:8080/faq/ -http://localhost:8080/societe/histoire/ \ No newline at end of file +${BASE_URL}/blog/ +${BASE_URL}/services/ +${BASE_URL} +${BASE_URL}/recrutement/ +${BASE_URL}/faq/ +${BASE_URL}/societe/histoire/ \ No newline at end of file diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 0000000..69a6d47 --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,53 @@ +apiVersion: skaffold/v3 +kind: Config + +metadata: + name: bouncer + +manifests: + kustomize: + paths: + - misc/k8s/kustomization/base + +profiles: +- name: dev + manifests: + kustomize: + paths: + - misc/k8s/kustomization/overlays/dev + activation: + - command: dev + +build: + local: + push: true + + tagPolicy: + sha256: {} + + artifacts: + - image: reg.cadoles.com/cadoles/bouncer + context: . + sync: + infer: + - cmd/** + - internal/** + - layers/** + - misc/** + docker: + dockerfile: Dockerfile + +deploy: + statusCheckDeadlineSeconds: 600 + +portForward: +- resourceType: service + resourceName: bouncer-admin + namespace: bouncer-dev + port: 8081 + localPort: 9999 +- resourceType: service + resourceName: bouncer-server + namespace: bouncer-dev + port: 8080 + localPort: 9000 # *Optional*