mirror of
https://github.com/sablierapp/sablier.git
synced 2025-12-21 13:23:03 +01:00
feat(plugins): add Caddy reverse proxy integration
This commit is contained in:
71
.github/workflows/plugins.yml
vendored
71
.github/workflows/plugins.yml
vendored
@@ -89,7 +89,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [docker_classic, docker_swarm, kubernetes]
|
||||
provider: [docker, docker_swarm] # , kubernetes]
|
||||
steps:
|
||||
- name: Set up Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
@@ -115,4 +115,71 @@ jobs:
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/nginx/e2e && bash ./${{ matrix.provider }}.sh
|
||||
run: cd plugins/nginx/e2e && bash ./${{ matrix.provider }}.sh
|
||||
|
||||
build-caddy:
|
||||
name: Build Caddy docker image once and share it to Caddy E2E jobs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: plugins/caddy
|
||||
file: plugins/caddy/Dockerfile
|
||||
tags: caddy:local
|
||||
outputs: type=docker,dest=/tmp/caddy.tar
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: caddy-image-tar
|
||||
path: /tmp/caddy.tar
|
||||
|
||||
caddy_e2e:
|
||||
name: Run Sablier E2E tests for Caddy middleware
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
- build-caddy
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [docker, docker_swarm] # , kubernetes]
|
||||
steps:
|
||||
- name: Set up Go 1.18
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Download Caddy artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: caddy-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Caddy Docker image
|
||||
run: docker load --input /tmp/caddy.tar
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/caddy/e2e && bash ./${{ matrix.provider }}.sh
|
||||
4
Makefile
4
Makefile
@@ -27,4 +27,6 @@ update-doc-version:
|
||||
|
||||
update-doc-version-middleware:
|
||||
find . -type f \( -name "*.md" -o -name "*.yml" \) -exec sed -i 's/version: "v$(LAST)"/version: "v$(NEXT)"/g' {} +
|
||||
find . -type f \( -name "*.md" -o -name "*.yml" \) -exec sed -i 's/version=v$(LAST)/version=v$(NEXT)/g' {} +
|
||||
find . -type f \( -name "*.md" -o -name "*.yml" \) -exec sed -i 's/version=v$(LAST)/version=v$(NEXT)/g' {} +
|
||||
sed -i 's/SABLIER_VERSION=v$(LAST)/SABLIER_VERSION=v$(NEXT)/g' plugins/caddy/Dockerfile.remote
|
||||
sed -i 's/v$(LAST)/v$(NEXT)/g' plugins/caddy/README.md
|
||||
|
||||
25
README.md
25
README.md
@@ -13,8 +13,6 @@ Which allows you to start your containers on demand and shut them down automatic
|
||||
- [Sablier](#sablier)
|
||||
- [Quick start with Traefik](#quick-start-with-traefik)
|
||||
- [Reverse proxies integration plugins](#reverse-proxies-integration-plugins)
|
||||
- [Traefik](#traefik)
|
||||
- [Nginx](#nginx)
|
||||
- [Guides](#guides)
|
||||
- [Sablier Guide: Code-Server + Traefik + Kubernetes Ingress](#sablier-guide-code-server--traefik--kubernetes-ingress)
|
||||
- [Configuration](#configuration)
|
||||
@@ -86,24 +84,11 @@ It leverage the API calls to Sablier to your reverse proxy middleware to wake up
|
||||
|
||||

|
||||
|
||||
| Reverse Proxy | Docker | Docker Swarm mode | Kubernetes | Podman |
|
||||
| ------------- | :-------------------------------------------------------: | :---------------: | :-----------: | :-------------------------------------------------------: |
|
||||
| Traefik | ✅ | ✅ | ✅ *(partial)* | [See #70](https://github.com/acouvreur/sablier/issues/70) |
|
||||
| Nginx | ✅ | ✅ | ❌ |
|
||||
| Apache | *Coming soon* |
|
||||
| Caddy | [See #67](https://github.com/acouvreur/sablier/issues/67) |
|
||||
|
||||
### Traefik
|
||||
|
||||
See [Traefik Middleware Plugin](https://github.com/acouvreur/sablier/tree/main/plugins/traefik/README.md)
|
||||
|
||||
- [Traefik Middleware Plugin with Docker classic](https://github.com/acouvreur/sablier/tree/main/plugins/traefik/README.md#traefik-with-docker-classic)
|
||||
- [Traefik Middleware Plugin with Docker Swarm](https://github.com/acouvreur/sablier/tree/main/plugins/traefik/README.md#traefik-with-docker-swarm)
|
||||
- [Traefik Middleware Plugin with Kubernetes](https://github.com/acouvreur/sablier/tree/main/plugins/traefik/README.md#traefik-with-kubernetes)
|
||||
|
||||
### Nginx
|
||||
|
||||
See [Nginx Middleware Plugin](/plugins/nginx/README.md)
|
||||
| Reverse Proxy | Docker | Docker Swarm mode | Kubernetes | Podman |
|
||||
| ----------------------------- | :-----------: | :---------------: | :-----------: | :-------------------------------------------------------: |
|
||||
| [Traefik](./plugins/traefik/) | ✅ | ✅ | ✅ *(partial)* | [See #70](https://github.com/acouvreur/sablier/issues/70) |
|
||||
| [Nginx](./plugins/nginx/) | ✅ | ✅ | ❌ |
|
||||
| [Caddy](./plugins/caddy/) | ✅ | ✅ | ❌ |
|
||||
|
||||
|
||||
## Guides
|
||||
|
||||
@@ -66,13 +66,6 @@ func Test_Multiple(t *testing.T) {
|
||||
func Test_Healthy(t *testing.T) {
|
||||
e := httpexpect.New(t, "http://localhost:8080/healthy/")
|
||||
|
||||
e.GET("/nginx").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Contains(`Healthy Nginx`).
|
||||
Contains(`Your instance(s) will stop after 1 minutes of inactivity`)
|
||||
|
||||
e.GET("/nginx").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
|
||||
315
go.work.sum
315
go.work.sum
@@ -1 +1,316 @@
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
|
||||
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
|
||||
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
|
||||
cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
|
||||
cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE=
|
||||
cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg=
|
||||
cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
|
||||
cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc=
|
||||
cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04=
|
||||
cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak=
|
||||
cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
|
||||
cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc=
|
||||
cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY=
|
||||
cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
|
||||
cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM=
|
||||
cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI=
|
||||
cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE=
|
||||
cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8=
|
||||
cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc=
|
||||
cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y=
|
||||
cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk=
|
||||
cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590=
|
||||
cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk=
|
||||
cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA=
|
||||
cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk=
|
||||
cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI=
|
||||
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
|
||||
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
|
||||
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
|
||||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
|
||||
cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
|
||||
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
|
||||
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
||||
cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo=
|
||||
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
|
||||
cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM=
|
||||
cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
|
||||
cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0=
|
||||
cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w=
|
||||
cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
|
||||
cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A=
|
||||
cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI=
|
||||
cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
|
||||
cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM=
|
||||
cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4=
|
||||
cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s=
|
||||
cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0=
|
||||
cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q=
|
||||
cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4=
|
||||
cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
|
||||
cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=
|
||||
cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU=
|
||||
cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8=
|
||||
cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw=
|
||||
cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI=
|
||||
cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=
|
||||
cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08=
|
||||
cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM=
|
||||
cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo=
|
||||
cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
|
||||
cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
|
||||
cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI=
|
||||
cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc=
|
||||
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
|
||||
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
|
||||
cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY=
|
||||
cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g=
|
||||
cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0=
|
||||
cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg=
|
||||
cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8=
|
||||
cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
|
||||
cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw=
|
||||
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
|
||||
cloud.google.com/go/longrunning v0.4.0/go.mod h1:eF3Qsw58iX/bkKtVjMTYpH0LRjQ2goDkjkNQTlzq/ZM=
|
||||
cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
|
||||
cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI=
|
||||
cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
|
||||
cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY=
|
||||
cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI=
|
||||
cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4=
|
||||
cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8=
|
||||
cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4=
|
||||
cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
|
||||
cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0=
|
||||
cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs=
|
||||
cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk=
|
||||
cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc=
|
||||
cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw=
|
||||
cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo=
|
||||
cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
|
||||
cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE=
|
||||
cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
|
||||
cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0=
|
||||
cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U=
|
||||
cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
|
||||
cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70=
|
||||
cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM=
|
||||
cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0=
|
||||
cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg=
|
||||
cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y=
|
||||
cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo=
|
||||
cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44=
|
||||
cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4=
|
||||
cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA=
|
||||
cloud.google.com/go/security v1.11.0/go.mod h1:qL8hSHb3MqXtsVRgSPOt/igsHrs5pWAy0nrP1zl4j5I=
|
||||
cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk=
|
||||
cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s=
|
||||
cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U=
|
||||
cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo=
|
||||
cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU=
|
||||
cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw=
|
||||
cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos=
|
||||
cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco=
|
||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
||||
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
|
||||
cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=
|
||||
cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA=
|
||||
cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4=
|
||||
cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg=
|
||||
cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y=
|
||||
cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg=
|
||||
cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw=
|
||||
cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU=
|
||||
cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E=
|
||||
cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g=
|
||||
cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208=
|
||||
cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8=
|
||||
cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A=
|
||||
cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ=
|
||||
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
|
||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
|
||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
||||
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
|
||||
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
|
||||
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo=
|
||||
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
|
||||
google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
|
||||
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo=
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
|
||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
|
||||
16
plugins/caddy/.gitignore
vendored
Normal file
16
plugins/caddy/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
traefik
|
||||
11
plugins/caddy/Dockerfile
Normal file
11
plugins/caddy/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
ARG CADDY_VERSION=2.6.4
|
||||
FROM caddy:${CADDY_VERSION}-builder AS builder
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/acouvreur/sablier/plugins/caddy=.
|
||||
|
||||
FROM caddy:${CADDY_VERSION}
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
12
plugins/caddy/Dockerfile.remote
Normal file
12
plugins/caddy/Dockerfile.remote
Normal file
@@ -0,0 +1,12 @@
|
||||
ARG CADDY_VERSION=2.6.4
|
||||
ARG SABLIER_VERSION=v1.4.0-beta.1
|
||||
FROM caddy:${CADDY_VERSION}-builder AS builder
|
||||
|
||||
ADD https://github.com/acouvreur/sablier.git#${SABLIER_VERSION} /sablier
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/acouvreur/sablier/plugins/caddy=/sablier/plugins/caddy
|
||||
|
||||
FROM caddy:${CADDY_VERSION}
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
91
plugins/caddy/README.md
Normal file
91
plugins/caddy/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Caddy Sablier Plugin
|
||||
|
||||
- [Caddy Sablier Plugin](#caddy-sablier-plugin)
|
||||
- [Build the custom Caddy image with Sablier middleware in it](#build-the-custom-caddy-image-with-sablier-middleware-in-it)
|
||||
- [By using the provided Dockerfile](#by-using-the-provided-dockerfile)
|
||||
- [By updating your Caddy Dockerfile](#by-updating-your-caddy-dockerfile)
|
||||
- [Configuration](#configuration)
|
||||
- [Exemple with a minimal configuration](#exemple-with-a-minimal-configuration)
|
||||
- [Running end to end tests](#running-end-to-end-tests)
|
||||
|
||||
## Build the custom Caddy image with Sablier middleware in it
|
||||
|
||||
In order to use the custom plugin for Caddy, you need to bundle it with Caddy.
|
||||
Here I'll show you two options with Docker.
|
||||
|
||||
### By using the provided Dockerfile
|
||||
|
||||
```
|
||||
docker build https://github.com/acouvreur/sablier.git#v1.4.0-beta.1:plugins/caddy
|
||||
--build-arg=CADDY_VERSION=2.6.4
|
||||
-t caddy:2.6.4-with-sablier
|
||||
```
|
||||
|
||||
**Note:** You can change `main` for any other branch (such as `beta`, or tags `v1.4.0-beta.1`)
|
||||
|
||||
### By updating your Caddy Dockerfile
|
||||
|
||||
```
|
||||
ARG CADDY_VERSION=2.6.4
|
||||
FROM caddy:${CADDY_VERSION}-builder AS builder
|
||||
|
||||
ADD https://github.com/acouvreur/sablier.git#v1.4.0-beta.1 /sablier
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/acouvreur/sablier/plugins/caddy=/sablier/plugins/caddy
|
||||
|
||||
FROM caddy:${CADDY_VERSION}
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You can have the following configuration:
|
||||
|
||||
```
|
||||
:80 {
|
||||
route /my/route {
|
||||
sablier [<sablierURL>=http://sablier:10000] {
|
||||
[names container1,container2,...]
|
||||
[group mygroup]
|
||||
[session_duration 30m]
|
||||
dynamic {
|
||||
[display_name This is my display name]
|
||||
[show_details yes|true|on]
|
||||
[theme hacker-terminal]
|
||||
[refresh_frequency 2s]
|
||||
}
|
||||
blocking {
|
||||
[timeout 1m]
|
||||
}
|
||||
}
|
||||
reverse_proxy myservice:port
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exemple with a minimal configuration
|
||||
|
||||
Almost all options are optional and you can setup very simple rules to use the server default values.
|
||||
|
||||
```
|
||||
:80 {
|
||||
route /my/route {
|
||||
sablier {
|
||||
group mygroup
|
||||
dynamic
|
||||
}
|
||||
reverse_proxy myservice:port
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Running end to end tests
|
||||
|
||||
1. Build local sablier
|
||||
`docker build -t caddy:local .`
|
||||
2. Build local caddy
|
||||
`docker build -t acouvreur/sablier:local ../..`
|
||||
3. Run test
|
||||
`cd e2e/docker && bash ./run.sh`
|
||||
278
plugins/caddy/config.go
Normal file
278
plugins/caddy/config.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package caddy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpcaddyfile.RegisterHandlerDirective("sablier", parseCaddyfile)
|
||||
|
||||
}
|
||||
|
||||
type DynamicConfiguration struct {
|
||||
DisplayName string
|
||||
ShowDetails *bool
|
||||
Theme string
|
||||
RefreshFrequency *time.Duration
|
||||
}
|
||||
|
||||
type BlockingConfiguration struct {
|
||||
Timeout *time.Duration
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
SablierURL string
|
||||
Names []string
|
||||
Group string
|
||||
SessionDuration *time.Duration
|
||||
Dynamic *DynamicConfiguration
|
||||
Blocking *BlockingConfiguration
|
||||
}
|
||||
|
||||
func CreateConfig() *Config {
|
||||
return &Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{},
|
||||
SessionDuration: nil,
|
||||
Dynamic: nil,
|
||||
Blocking: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
|
||||
//
|
||||
// sablier [<sablierURL>] {
|
||||
// [names container1,container2,...]
|
||||
// [group mygroup]
|
||||
// [session_duration 30m]
|
||||
// dynamic {
|
||||
// [display_name This is my display name]
|
||||
// [show_details yes|true|on]
|
||||
// [theme hacker-terminal]
|
||||
// [refresh_frequency 2s]
|
||||
// }
|
||||
// blocking {
|
||||
// [timeout 1m]
|
||||
// }
|
||||
// }
|
||||
//
|
||||
func (c *Config) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
for d.Next() {
|
||||
if d.NextArg() {
|
||||
c.SablierURL = d.Val()
|
||||
} else {
|
||||
c.SablierURL = "http://sablier:10000"
|
||||
}
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
subdirective := d.Val()
|
||||
args := strings.Join(d.RemainingArgs(), " ")
|
||||
switch subdirective {
|
||||
case "names":
|
||||
c.Names = parseNames(args)
|
||||
case "group":
|
||||
c.Group = args
|
||||
case "session_duration":
|
||||
duration, err := time.ParseDuration(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.SessionDuration = &duration
|
||||
case "dynamic":
|
||||
dynamic, err := parseDynamic(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Dynamic = dynamic
|
||||
case "blocking":
|
||||
blocking, err := parseBlocking(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Blocking = blocking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.Blocking == nil && c.Dynamic == nil {
|
||||
return fmt.Errorf("you must specify one strategy (dynamic or blocking)")
|
||||
}
|
||||
|
||||
if c.Blocking != nil && c.Dynamic != nil {
|
||||
return fmt.Errorf("you must specify only one strategy")
|
||||
}
|
||||
|
||||
if len(c.Names) == 0 && len(c.Group) == 0 {
|
||||
return fmt.Errorf("you must specify names or group")
|
||||
}
|
||||
|
||||
if len(c.Names) > 0 && len(c.Group) > 0 {
|
||||
return fmt.Errorf("you must specify either names or group")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseNames(value string) []string {
|
||||
names := strings.Split(value, " ")
|
||||
for i := range names {
|
||||
names[i] = strings.TrimSpace(names[i])
|
||||
}
|
||||
|
||||
if len(names) == 1 && names[0] == "" {
|
||||
return make([]string, 0)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func parseDynamic(d *caddyfile.Dispenser) (*DynamicConfiguration, error) {
|
||||
conf := &DynamicConfiguration{}
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
subdirective := d.Val()
|
||||
args := strings.Join(d.RemainingArgs(), " ")
|
||||
switch subdirective {
|
||||
case "display_name":
|
||||
conf.DisplayName = args
|
||||
case "show_details":
|
||||
shouldShow := isEnabledArg(args)
|
||||
conf.ShowDetails = &shouldShow
|
||||
case "theme":
|
||||
conf.Theme = args
|
||||
case "refresh_frequency":
|
||||
duration, err := time.ParseDuration(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.RefreshFrequency = &duration
|
||||
}
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func parseBlocking(d *caddyfile.Dispenser) (*BlockingConfiguration, error) {
|
||||
conf := &BlockingConfiguration{}
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
subdirective := d.Val()
|
||||
args := strings.Join(d.RemainingArgs(), " ")
|
||||
switch subdirective {
|
||||
case "timeout":
|
||||
duration, err := time.ParseDuration(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.Timeout = &duration
|
||||
}
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
var c Config
|
||||
err := c.UnmarshalCaddyfile(h.Dispenser)
|
||||
return SablierMiddleware{Config: c}, err
|
||||
}
|
||||
|
||||
func isEnabledArg(s string) bool {
|
||||
if s == "yes" || s == "true" || s == "on" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Config) BuildRequest() (*http.Request, error) {
|
||||
if c.Dynamic != nil {
|
||||
return c.buildDynamicRequest()
|
||||
} else if c.Blocking != nil {
|
||||
return c.buildBlockingRequest()
|
||||
}
|
||||
return nil, fmt.Errorf("no strategy configured")
|
||||
}
|
||||
|
||||
func (c *Config) buildDynamicRequest() (*http.Request, error) {
|
||||
if c.Dynamic == nil {
|
||||
return nil, fmt.Errorf("dynamic config is nil")
|
||||
}
|
||||
|
||||
request, err := http.NewRequest("GET", fmt.Sprintf("%s/api/strategies/dynamic", c.SablierURL), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := request.URL.Query()
|
||||
|
||||
if c.SessionDuration != nil {
|
||||
q.Add("session_duration", c.SessionDuration.String())
|
||||
}
|
||||
|
||||
for _, name := range c.Names {
|
||||
q.Add("names", name)
|
||||
}
|
||||
|
||||
if c.Group != "" {
|
||||
q.Add("group", c.Group)
|
||||
}
|
||||
|
||||
if c.Dynamic.DisplayName != "" {
|
||||
q.Add("display_name", c.Dynamic.DisplayName)
|
||||
}
|
||||
|
||||
if c.Dynamic.Theme != "" {
|
||||
q.Add("theme", c.Dynamic.Theme)
|
||||
}
|
||||
|
||||
if c.Dynamic.RefreshFrequency != nil {
|
||||
q.Add("refresh_frequency", c.Dynamic.RefreshFrequency.String())
|
||||
}
|
||||
|
||||
if c.Dynamic.ShowDetails != nil {
|
||||
q.Add("show_details", strconv.FormatBool(*c.Dynamic.ShowDetails))
|
||||
}
|
||||
|
||||
request.URL.RawQuery = q.Encode()
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (c *Config) buildBlockingRequest() (*http.Request, error) {
|
||||
if c.Blocking == nil {
|
||||
return nil, fmt.Errorf("blocking config is nil")
|
||||
}
|
||||
|
||||
request, err := http.NewRequest("GET", fmt.Sprintf("%s/api/strategies/blocking", c.SablierURL), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := request.URL.Query()
|
||||
|
||||
if c.SessionDuration != nil {
|
||||
q.Add("session_duration", c.SessionDuration.String())
|
||||
}
|
||||
|
||||
for _, name := range c.Names {
|
||||
q.Add("names", name)
|
||||
}
|
||||
|
||||
if c.Group != "" {
|
||||
q.Add("group", c.Group)
|
||||
}
|
||||
|
||||
if c.Blocking.Timeout != nil {
|
||||
q.Add("timeout", c.Blocking.Timeout.String())
|
||||
}
|
||||
|
||||
request.URL.RawQuery = q.Encode()
|
||||
|
||||
return request, nil
|
||||
}
|
||||
450
plugins/caddy/config_test.go
Normal file
450
plugins/caddy/config_test.go
Normal file
@@ -0,0 +1,450 @@
|
||||
package caddy_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/acouvreur/sablier/plugins/caddy"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
)
|
||||
|
||||
var fals bool = false
|
||||
var tru bool = true
|
||||
var oneMinute = 1 * time.Minute
|
||||
|
||||
func TestConfig_BuildRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fields caddy.Config
|
||||
want *http.Request
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "dynamic session with required values",
|
||||
fields: caddy.Config{
|
||||
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
Dynamic: &caddy.DynamicConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with default values",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with group",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "default",
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?group=default&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with theme values",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
Theme: "hacker-terminal",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache&session_duration=1m&theme=hacker-terminal", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with theme and display name values",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
Theme: "hacker-terminal",
|
||||
DisplayName: "Hello World!",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=Hello+World%21&names=nginx&names=apache&session_duration=1m&theme=hacker-terminal", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with refresh frequency",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
RefreshFrequency: &oneMinute,
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache&refresh_frequency=1m&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with show details to true",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
ShowDetails: &tru,
|
||||
RefreshFrequency: &oneMinute,
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache&refresh_frequency=1m&session_duration=1m&show_details=true", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with show details to false",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
ShowDetails: &fals,
|
||||
RefreshFrequency: &oneMinute,
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache&refresh_frequency=1m&session_duration=1m&show_details=false", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session without show details set",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
ShowDetails: nil,
|
||||
RefreshFrequency: &oneMinute,
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?names=nginx&names=apache&refresh_frequency=1m&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with required values",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
Blocking: &caddy.BlockingConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with default values",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Blocking: &caddy.BlockingConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with group",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "default",
|
||||
SessionDuration: &oneMinute,
|
||||
Blocking: &caddy.BlockingConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?group=default&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with timeout value",
|
||||
fields: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"nginx", "apache"},
|
||||
SessionDuration: &oneMinute,
|
||||
Blocking: &caddy.BlockingConfiguration{
|
||||
Timeout: nil,
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache&session_duration=1m&timeout=5m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &caddy.Config{
|
||||
SablierURL: tt.fields.SablierURL,
|
||||
Names: tt.fields.Names,
|
||||
Group: tt.fields.Group,
|
||||
SessionDuration: tt.fields.SessionDuration,
|
||||
Dynamic: tt.fields.Dynamic,
|
||||
Blocking: tt.fields.Blocking,
|
||||
}
|
||||
|
||||
got, err := c.BuildRequest()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Config.BuildRequest() error = %v, wantErr %v", err, tt.wantErr)
|
||||
} else if got.RequestURI != tt.want.RequestURI {
|
||||
t.Errorf("Config.BuildRequest() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_UnmarshalCaddyfile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want caddy.Config
|
||||
wantErr bool
|
||||
wantErrValue string
|
||||
}{
|
||||
{
|
||||
name: "default sablier URL",
|
||||
input: `sablier {
|
||||
group mygroup
|
||||
dynamic
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "mygroup",
|
||||
Dynamic: &caddy.DynamicConfiguration{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "specific sablier URL",
|
||||
input: `sablier http://mysablier:3000 {
|
||||
names container1 container2 container3
|
||||
blocking
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://mysablier:3000",
|
||||
Names: []string{"container1", "container2", "container3"},
|
||||
Blocking: &caddy.BlockingConfiguration{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "parse valid names dynamic",
|
||||
input: `sablier {
|
||||
names container1 container2 container3
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name This is a display name!
|
||||
show_details on
|
||||
theme hacker-terminal
|
||||
refresh_frequency 1m
|
||||
}
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: []string{"container1", "container2", "container3"},
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
DisplayName: "This is a display name!",
|
||||
ShowDetails: &tru,
|
||||
Theme: "hacker-terminal",
|
||||
RefreshFrequency: &oneMinute,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "parse valid group dynamic",
|
||||
input: `sablier {
|
||||
group mygroup
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name This is a display name!
|
||||
show_details on
|
||||
theme hacker-terminal
|
||||
refresh_frequency 1m
|
||||
}
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "mygroup",
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &caddy.DynamicConfiguration{
|
||||
DisplayName: "This is a display name!",
|
||||
ShowDetails: &tru,
|
||||
Theme: "hacker-terminal",
|
||||
RefreshFrequency: &oneMinute,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "parse valid names blocking",
|
||||
input: `sablier {
|
||||
group mygroup
|
||||
session_duration 1m
|
||||
blocking {
|
||||
timeout 1m
|
||||
}
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "mygroup",
|
||||
SessionDuration: &oneMinute,
|
||||
Blocking: &caddy.BlockingConfiguration{
|
||||
Timeout: &oneMinute,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "parse invalid no strategies",
|
||||
input: `sablier`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "you must specify one strategy (dynamic or blocking)",
|
||||
},
|
||||
{
|
||||
name: "parse invalid two strategies",
|
||||
input: `sablier {
|
||||
blocking
|
||||
dynamic
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "you must specify only one strategy",
|
||||
},
|
||||
{
|
||||
name: "parse invalid no names or group",
|
||||
input: `sablier {
|
||||
blocking
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "you must specify names or group",
|
||||
},
|
||||
{
|
||||
name: "parse empty names",
|
||||
input: `sablier {
|
||||
names
|
||||
blocking
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "you must specify names or group",
|
||||
},
|
||||
{
|
||||
name: "parse empty group",
|
||||
input: `sablier {
|
||||
group
|
||||
blocking
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "you must specify names or group",
|
||||
},
|
||||
{
|
||||
name: "parse invalid names and group",
|
||||
input: `sablier {
|
||||
names container1 container2
|
||||
group mygroup
|
||||
blocking
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "you must specify either names or group",
|
||||
},
|
||||
{
|
||||
name: "parse invalid session_duration",
|
||||
input: `sablier {
|
||||
group mygroup
|
||||
session_duration invalid
|
||||
blocking
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "time: invalid duration \"invalid\"",
|
||||
},
|
||||
{
|
||||
name: "parse invalid refresh_frequency",
|
||||
input: `sablier {
|
||||
group mygroup
|
||||
dynamic {
|
||||
refresh_frequency invalid
|
||||
}
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "time: invalid duration \"invalid\"",
|
||||
},
|
||||
{
|
||||
name: "parse invalid timeout",
|
||||
input: `sablier {
|
||||
group mygroup
|
||||
blocking {
|
||||
timeout invalid
|
||||
}
|
||||
}`,
|
||||
want: caddy.Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
},
|
||||
wantErr: true,
|
||||
wantErrValue: "time: invalid duration \"invalid\"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
h := httpcaddyfile.Helper{
|
||||
Dispenser: caddyfile.NewTestDispenser(tt.input),
|
||||
}
|
||||
got := caddy.Config{}
|
||||
err := got.UnmarshalCaddyfile(h.Dispenser)
|
||||
|
||||
if tt.wantErr {
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("%s: UnmarshalCaddyfile() error = %v, wantErr = %v", tt.name, err, tt.wantErr)
|
||||
}
|
||||
if err.Error() != tt.wantErrValue {
|
||||
t.Errorf("%s: UnmarshalCaddyfile() error = %v, wantErrValue = %v", tt.name, err.Error(), tt.wantErrValue)
|
||||
}
|
||||
} else if !reflect.DeepEqual(tt.want, got) {
|
||||
t.Errorf("%s: UnmarshalCaddyfile() = %v, want %v", tt.name, got, tt.want)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func createRequest(method string, url string, body io.Reader) *http.Request {
|
||||
request, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return request
|
||||
}
|
||||
60
plugins/caddy/e2e/docker/Caddyfile
Normal file
60
plugins/caddy/e2e/docker/Caddyfile
Normal file
@@ -0,0 +1,60 @@
|
||||
:80 {
|
||||
route /dynamic/whoami {
|
||||
sablier http://sablier:10000 {
|
||||
names docker_classic_e2e-whoami-1
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Dynamic Whoami
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /blocking/whoami {
|
||||
sablier http://sablier:10000 {
|
||||
names docker_classic_e2e-whoami-1
|
||||
session_duration 1m
|
||||
blocking {
|
||||
timeout 30s
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /multiple/whoami {
|
||||
sablier http://sablier:10000 {
|
||||
names docker_classic_e2e-whoami-1 docker_classic_e2e-nginx-1
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Multiple Whoami
|
||||
theme=hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /multiple/nginx {
|
||||
sablier http://sablier:10000 {
|
||||
names docker_classic_e2e-whoami-1 docker_classic_e2e-nginx-1
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Multiple Whoami
|
||||
theme=hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
|
||||
route /healthy/nginx {
|
||||
sablier http://sablier:10000 {
|
||||
names docker_classic_e2e-nginx-1
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Healthy Nginx
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
}
|
||||
28
plugins/caddy/e2e/docker/docker-compose.yml
Normal file
28
plugins/caddy/e2e/docker/docker-compose.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
proxy:
|
||||
image: caddy:local
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
restart: "no"
|
||||
|
||||
sablier:
|
||||
image: acouvreur/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=trace
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: containous/whoami:v1.5.0
|
||||
|
||||
nginx:
|
||||
image: nginx:1.23.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
39
plugins/caddy/e2e/docker/run.sh
Normal file
39
plugins/caddy/e2e/docker/run.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOCKER_COMPOSE_FILE=docker-compose.yml
|
||||
DOCKER_COMPOSE_PROJECT_NAME=docker_classic_e2e
|
||||
|
||||
errors=0
|
||||
|
||||
echo "Using Docker version:"
|
||||
docker version
|
||||
|
||||
prepare_docker_classic() {
|
||||
docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME up -d
|
||||
docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME stop whoami nginx
|
||||
}
|
||||
|
||||
destroy_docker_classic() {
|
||||
docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME down --remove-orphans || true
|
||||
}
|
||||
|
||||
run_docker_classic_test() {
|
||||
echo "Running Docker Classic Test: $1"
|
||||
prepare_docker_classic
|
||||
sleep 2
|
||||
go clean -testcache
|
||||
if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/acouvreur/sablier/e2e; then
|
||||
errors=1
|
||||
docker compose -f ${DOCKER_COMPOSE_FILE} -p ${DOCKER_COMPOSE_PROJECT_NAME} logs sablier proxy
|
||||
fi
|
||||
destroy_docker_classic
|
||||
}
|
||||
|
||||
trap destroy_docker_classic EXIT
|
||||
|
||||
run_docker_classic_test Test_Dynamic
|
||||
run_docker_classic_test Test_Blocking
|
||||
run_docker_classic_test Test_Multiple
|
||||
run_docker_classic_test Test_Healthy
|
||||
|
||||
exit $errors
|
||||
60
plugins/caddy/e2e/docker_swarm/Caddyfile
Normal file
60
plugins/caddy/e2e/docker_swarm/Caddyfile
Normal file
@@ -0,0 +1,60 @@
|
||||
:80 {
|
||||
route /dynamic/whoami {
|
||||
sablier http://tasks.sablier:10000 {
|
||||
names DOCKER_SWARM_E2E_whoami
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Dynamic Whoami
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /blocking/whoami {
|
||||
sablier http://tasks.sablier:10000 {
|
||||
names DOCKER_SWARM_E2E_whoami
|
||||
session_duration 1m
|
||||
blocking {
|
||||
timeout 30s
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /multiple/whoami {
|
||||
sablier http://tasks.sablier:10000 {
|
||||
names DOCKER_SWARM_E2E_whoami DOCKER_SWARM_E2E_nginx
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Multiple Whoami
|
||||
theme=hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /multiple/nginx {
|
||||
sablier http://tasks.sablier:10000 {
|
||||
names DOCKER_SWARM_E2E_whoami DOCKER_SWARM_E2E_nginx
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Multiple Whoami
|
||||
theme=hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
|
||||
route /healthy/nginx {
|
||||
sablier http://tasks.sablier:10000 {
|
||||
names DOCKER_SWARM_E2E_nginx
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Healthy Nginx
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
}
|
||||
37
plugins/caddy/e2e/docker_swarm/docker-stack.yml
Normal file
37
plugins/caddy/e2e/docker_swarm/docker-stack.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
proxy:
|
||||
image: caddy:local
|
||||
ports:
|
||||
- target: 80
|
||||
published: 8080
|
||||
protocol: tcp
|
||||
mode: host # Won't work in github actions otherwise
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: none # Do not restart on setup failure
|
||||
|
||||
sablier:
|
||||
image: acouvreur/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=swarm
|
||||
- --logging.level=trace
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: containous/whoami:v1.5.0
|
||||
deploy:
|
||||
replicas: 0
|
||||
|
||||
nginx:
|
||||
image: nginx:1.23.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
deploy:
|
||||
replicas: 0
|
||||
52
plugins/caddy/e2e/docker_swarm/run.sh
Normal file
52
plugins/caddy/e2e/docker_swarm/run.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOCKER_STACK_FILE=docker-stack.yml
|
||||
DOCKER_STACK_NAME=DOCKER_SWARM_E2E
|
||||
|
||||
errors=0
|
||||
|
||||
echo "Using Docker version:"
|
||||
docker version
|
||||
|
||||
prepare_docker_swarm() {
|
||||
docker swarm init
|
||||
}
|
||||
|
||||
prepare_docker_stack() {
|
||||
docker stack deploy --compose-file $DOCKER_STACK_FILE ${DOCKER_STACK_NAME}
|
||||
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock sudobmitch/docker-stack-wait -t 60 ${DOCKER_STACK_NAME}
|
||||
}
|
||||
|
||||
destroy_docker_stack() {
|
||||
docker stack rm ${DOCKER_STACK_NAME}
|
||||
# Sometimes, the network is not well cleaned up, see https://github.com/moby/moby/issues/30942#issuecomment-540699206
|
||||
until [ -z "$(docker stack ps ${DOCKER_STACK_NAME} -q)" ]; do sleep 1; done
|
||||
}
|
||||
|
||||
destroy_docker_swarm() {
|
||||
docker swarm leave -f || true
|
||||
}
|
||||
|
||||
run_docker_swarm_test() {
|
||||
echo "Running Docker Swarm Test: $1"
|
||||
prepare_docker_stack
|
||||
sleep 10
|
||||
go clean -testcache
|
||||
if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/acouvreur/sablier/e2e; then
|
||||
errors=1
|
||||
docker service logs ${DOCKER_STACK_NAME}_sablier
|
||||
docker service logs ${DOCKER_STACK_NAME}_proxy
|
||||
fi
|
||||
destroy_docker_stack
|
||||
}
|
||||
|
||||
trap destroy_docker_swarm EXIT
|
||||
|
||||
prepare_docker_swarm
|
||||
prepare_docker_stack
|
||||
run_docker_swarm_test Test_Dynamic
|
||||
run_docker_swarm_test Test_Blocking
|
||||
run_docker_swarm_test Test_Multiple
|
||||
run_docker_swarm_test Test_Healthy
|
||||
|
||||
exit $errors
|
||||
22
plugins/caddy/e2e/kubernetes/Caddyfile
Normal file
22
plugins/caddy/e2e/kubernetes/Caddyfile
Normal file
@@ -0,0 +1,22 @@
|
||||
:80 {
|
||||
route /dynamic/whoami {
|
||||
sablier url=http://tasks.sablier:10000 names=e2e-whoami-1 session_duration=1m dynamic.display_name=Dynamic-Whoami dynamic.theme=hacker-terminal
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /blocking/whoami {
|
||||
sablier url=http://tasks.sablier:10000 names=e2e-whoami-1 session_duration=1m blocking.timeout=30s
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
|
||||
route /multiple {
|
||||
sablier url=http://tasks.sablier:10000 names=e2e-whoami-1,e2e-nginx-1 session_duration=1m dynamic.display_name=Multiple-Whoami dynamic.theme=hacker-terminal
|
||||
reverse_proxy /multiple/whoami whoami:80
|
||||
reverse_proxy /multiple/nginx nginx:80
|
||||
}
|
||||
|
||||
route /healthy/nginx {
|
||||
sablier url=http://tasks.sablier:10000 names=e2e-nginx-1 session_duration=1m dynamic.display_name=Healthy-Nginx dynamic.theme=hacker-terminal
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
}
|
||||
25
plugins/caddy/e2e/kubernetes/docker-kubernetes.yml
Normal file
25
plugins/caddy/e2e/kubernetes/docker-kubernetes.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: '3'
|
||||
services:
|
||||
server:
|
||||
image: "rancher/k3s:v1.23.12-k3s1"
|
||||
command: server --no-deploy traefik
|
||||
tmpfs:
|
||||
- /run
|
||||
- /var/run
|
||||
ulimits:
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 65535
|
||||
hard: 65535
|
||||
privileged: true
|
||||
restart: always
|
||||
environment:
|
||||
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
|
||||
- K3S_KUBECONFIG_MODE=666
|
||||
volumes:
|
||||
# This is just so that we get the kubeconfig file out
|
||||
- .:/output
|
||||
- '../../..:/plugins-local/src/github.com/acouvreur/sablier'
|
||||
ports:
|
||||
- 6443:6443 # Kubernetes API Server
|
||||
- 8080:80 # Ingress controller port 80
|
||||
66
plugins/caddy/e2e/kubernetes/run.sh
Normal file
66
plugins/caddy/e2e/kubernetes/run.sh
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
export DOCKER_COMPOSE_FILE=docker-kubernetes.yml
|
||||
export DOCKER_COMPOSE_PROJECT_NAME=kubernetes_e2e
|
||||
|
||||
errors=0
|
||||
|
||||
export KUBECONFIG=./kubeconfig.yaml
|
||||
|
||||
echo "Using Docker version:"
|
||||
docker version
|
||||
|
||||
prepare_kubernetes() {
|
||||
docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME up -d
|
||||
until kubectl get nodes | grep " Ready "; do sleep 1; done
|
||||
echo "Loading acouvreur/sablier:local into k3s..."
|
||||
docker save acouvreur/sablier:local | docker exec -i ${DOCKER_COMPOSE_PROJECT_NAME}-server-1 ctr images import -
|
||||
echo "Loading succeeded."
|
||||
}
|
||||
|
||||
destroy_kubernetes() {
|
||||
docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME down --volumes
|
||||
}
|
||||
|
||||
prepare_deployment() {
|
||||
kubectl apply -f ./manifests/sablier.yml
|
||||
kubectl apply -f ./manifests/deployment.yml
|
||||
}
|
||||
|
||||
destroy_deployment() {
|
||||
kubectl delete -f ./manifests/deployment.yml
|
||||
kubectl delete -f ./manifests/sablier.yml
|
||||
}
|
||||
|
||||
prepare_stateful_set() {
|
||||
kubectl apply -f ./manifests/statefulset.yml
|
||||
}
|
||||
|
||||
destroy_stateful_set() {
|
||||
kubectl delete -f ./manifests/statefulset.yml
|
||||
}
|
||||
|
||||
run_kubernetes_deployment_test() {
|
||||
echo "---- Running Kubernetes Test: $1 ----"
|
||||
prepare_deployment
|
||||
sleep 10
|
||||
go clean -testcache
|
||||
if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/acouvreur/sablier/e2e; then
|
||||
errors=1
|
||||
kubectl -n kube-system logs deployments/sablier-deployment
|
||||
# kubectl -n kube-system logs deployments/caddy
|
||||
fi
|
||||
|
||||
destroy_deployment
|
||||
}
|
||||
|
||||
trap destroy_kubernetes EXIT
|
||||
|
||||
prepare_kubernetes
|
||||
prepare_caddy # TODO: Implement this, will fail for now
|
||||
# run_kubernetes_deployment_test Test_Dynamic
|
||||
# run_kubernetes_deployment_test Test_Blocking # Blocking is not yet supported
|
||||
# run_kubernetes_deployment_test Test_Multiple
|
||||
# run_kubernetes_deployment_test Test_Healthy
|
||||
|
||||
exit $errors
|
||||
25
plugins/caddy/e2e/kubernetes/values.yaml
Normal file
25
plugins/caddy/e2e/kubernetes/values.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# traefik helm values
|
||||
image:
|
||||
tag: "2.9.1"
|
||||
|
||||
additionalArguments:
|
||||
- "--experimental.localPlugins.sablier.moduleName=github.com/acouvreur/sablier"
|
||||
|
||||
providers:
|
||||
kubernetesIngress:
|
||||
allowEmptyServices: true
|
||||
kubernetesCRD:
|
||||
allowEmptyServices: true
|
||||
|
||||
additionalVolumeMounts:
|
||||
- name: local-sablier-plugin
|
||||
mountPath: /plugins-local/src/github.com/acouvreur/sablier
|
||||
|
||||
deployment:
|
||||
additionalVolumes:
|
||||
- name: local-sablier-plugin
|
||||
hostPath:
|
||||
# directory location on host
|
||||
path: /plugins-local/src/github.com/acouvreur/sablier
|
||||
# this field is optional
|
||||
type: Directory
|
||||
109
plugins/caddy/go.mod
Normal file
109
plugins/caddy/go.mod
Normal file
@@ -0,0 +1,109 @@
|
||||
module github.com/acouvreur/sablier/plugins/caddy
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/caddyserver/caddy/v2 v2.6.4
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/caddyserver/certmagic v0.17.2 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-kit/kit v0.10.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/cel-go v0.13.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.13.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.12.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.17.2 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/acmez v1.1.0 // indirect
|
||||
github.com/micromdm/scep/v2 v2.1.0 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/slackhq/nebula v1.6.1 // indirect
|
||||
github.com/smallstep/certificates v0.23.2 // indirect
|
||||
github.com/smallstep/nosql v0.5.0 // indirect
|
||||
github.com/smallstep/truststore v0.12.1 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.6.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20230124224810-c6dc1f4049b2 // indirect
|
||||
github.com/urfave/cli v1.22.12 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||
go.step.sm/cli-utils v0.7.5 // indirect
|
||||
go.step.sm/crypto v0.23.2 // indirect
|
||||
go.step.sm/linkedca v0.19.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
|
||||
google.golang.org/grpc v1.52.3 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
||||
1087
plugins/caddy/go.sum
Normal file
1087
plugins/caddy/go.sum
Normal file
File diff suppressed because it is too large
Load Diff
73
plugins/caddy/main.go
Normal file
73
plugins/caddy/main.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package caddy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterModule(SablierMiddleware{})
|
||||
}
|
||||
|
||||
type SablierMiddleware struct {
|
||||
Config Config
|
||||
client *http.Client
|
||||
request *http.Request
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (SablierMiddleware) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: "http.handlers.sablier",
|
||||
New: func() caddy.Module { return new(SablierMiddleware) },
|
||||
}
|
||||
}
|
||||
|
||||
// Provision implements caddy.Provisioner.
|
||||
func (m *SablierMiddleware) Provision(ctx caddy.Context) error {
|
||||
req, err := m.Config.BuildRequest()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.request = req
|
||||
m.client = &http.Client{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServeHTTP implements caddyhttp.MiddlewareHandler.
|
||||
func (sm SablierMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request, next caddyhttp.Handler) error {
|
||||
sablierRequest := sm.request.Clone(context.TODO())
|
||||
|
||||
resp, err := sm.client.Do(sablierRequest)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.Header.Get("X-Sablier-Session-Status") == "ready" {
|
||||
next.ServeHTTP(rw, req)
|
||||
} else {
|
||||
forward(resp, rw)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func forward(resp *http.Response, rw http.ResponseWriter) {
|
||||
rw.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
|
||||
rw.Header().Set("Content-Length", resp.Header.Get("Content-Length"))
|
||||
io.Copy(rw, resp.Body)
|
||||
}
|
||||
|
||||
// Interface guards
|
||||
var (
|
||||
_ caddy.Provisioner = (*SablierMiddleware)(nil)
|
||||
_ caddyhttp.MiddlewareHandler = (*SablierMiddleware)(nil)
|
||||
)
|
||||
108
plugins/caddy/main_test.go
Normal file
108
plugins/caddy/main_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package caddy_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
plugin "github.com/acouvreur/sablier/plugins/caddy"
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
func TestSablierMiddleware_ServeHTTP(t *testing.T) {
|
||||
type fields struct {
|
||||
Next caddyhttp.Handler
|
||||
SablierMiddleware *plugin.SablierMiddleware
|
||||
}
|
||||
type sablier struct {
|
||||
headers map[string]string
|
||||
body string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
sablier sablier
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "sablier service is ready",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
_, err := fmt.Fprint(w, "response from service")
|
||||
return err
|
||||
}),
|
||||
SablierMiddleware: &plugin.SablierMiddleware{
|
||||
Config: plugin.Config{
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &plugin.DynamicConfiguration{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "response from service",
|
||||
},
|
||||
{
|
||||
name: "sablier service is not ready",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "not-ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
_, err := fmt.Fprint(w, "response from service")
|
||||
return err
|
||||
}),
|
||||
SablierMiddleware: &plugin.SablierMiddleware{
|
||||
Config: plugin.Config{
|
||||
SessionDuration: &oneMinute,
|
||||
Dynamic: &plugin.DynamicConfiguration{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "response from sablier",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sablierMockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
for key, value := range tt.sablier.headers {
|
||||
w.Header().Add(key, value)
|
||||
}
|
||||
w.Write([]byte(tt.sablier.body))
|
||||
}))
|
||||
defer sablierMockServer.Close()
|
||||
|
||||
tt.fields.SablierMiddleware.Config.SablierURL = sablierMockServer.URL
|
||||
|
||||
err := tt.fields.SablierMiddleware.Provision(caddy.Context{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/my-nginx", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
tt.fields.SablierMiddleware.ServeHTTP(w, req, tt.fields.Next)
|
||||
|
||||
res := w.Result()
|
||||
defer res.Body.Close()
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Errorf("expected error to be nil got %v", err)
|
||||
}
|
||||
if string(data) != tt.expected {
|
||||
t.Errorf("expected %s got %v", tt.expected, string(data))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ destroy_kubernetes() {
|
||||
docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME down --volumes
|
||||
}
|
||||
|
||||
|
||||
prepare_deployment() {
|
||||
kubectl apply -f ./manifests/sablier.yml
|
||||
kubectl apply -f ./manifests/deployment.yml
|
||||
|
||||
Reference in New Issue
Block a user