mirror of
https://github.com/sablierapp/sablier.git
synced 2025-12-21 13:23:03 +01:00
fix\!: remove plugins from the repository
This commit is contained in:
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@@ -3,9 +3,6 @@ updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directories:
|
||||
- "/"
|
||||
- "/plugins/caddy"
|
||||
- "/plugins/proxywasm"
|
||||
- "/plugins/traefik"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
@@ -22,15 +19,11 @@ updates:
|
||||
- "k8s.io/cli-runtime"
|
||||
- "k8s.io/client-go"
|
||||
- "k8s.io/kubectl"
|
||||
assignees:
|
||||
- "acouvreur"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
assignees:
|
||||
- "acouvreur"
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
@@ -40,5 +33,3 @@ updates:
|
||||
# Check for updates once a week
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
assignees:
|
||||
- "acouvreur"
|
||||
|
||||
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
@@ -2,10 +2,6 @@ documentation:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'docs/**'
|
||||
|
||||
reverse-proxy:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'plugins/**'
|
||||
|
||||
provider:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'pkg/provider/**'
|
||||
|
||||
360
.github/workflows/plugins.yml
vendored
360
.github/workflows/plugins.yml
vendored
@@ -1,360 +0,0 @@
|
||||
name: Build Sablier plugins
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Sablier docker image once and share it to E2E jobs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
tags: sablierapp/sablier:local
|
||||
outputs: type=docker,dest=/tmp/sablier.tar
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp/sablier.tar
|
||||
|
||||
traefik:
|
||||
name: Build Traefik Sablier Plugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: plugins/traefik/go.sum
|
||||
|
||||
- name: Build
|
||||
run: cd plugins/traefik && go build -v .
|
||||
|
||||
- name: Test
|
||||
run: cd plugins/traefik && go test -v ./...
|
||||
|
||||
traefik_e2e:
|
||||
name: Run Sablier E2E tests for Traefik middleware
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [docker, docker_swarm, kubernetes]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
plugins/traefik/go.sum
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/traefik/e2e/${{ matrix.provider }} && bash ./run.sh
|
||||
|
||||
nginx_e2e:
|
||||
name: Run Sablier E2E tests for Nginx NJS module with Sablier
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [docker, docker_swarm] # , kubernetes]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/nginx/e2e/${{ matrix.provider }} && bash ./run.sh
|
||||
|
||||
caddy:
|
||||
name: Build Caddy Sablier Plugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: plugins/caddy/go.sum
|
||||
|
||||
- name: Build
|
||||
run: cd plugins/caddy && go build -v .
|
||||
|
||||
- name: Test
|
||||
run: cd plugins/caddy && go test -v ./...
|
||||
|
||||
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@v5
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
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@v4
|
||||
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: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
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@v5
|
||||
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/${{ matrix.provider }} && bash ./run.sh
|
||||
|
||||
build-proxywasm:
|
||||
name: Build ProxyWasm Plugin once and share it to ProxyWasm E2E jobs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.24
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24
|
||||
cache-dependency-path: |
|
||||
plugins/proxywasm/go.sum
|
||||
|
||||
- name: Build
|
||||
run: make proxywasm
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: proxywasm-plugin-wasm
|
||||
path: ./plugins/proxywasm/sablierproxywasm.wasm
|
||||
|
||||
proxywasm_apisix_e2e:
|
||||
name: Run Sablier E2E tests for Proxywasm middleware on Apache APISIX
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
- build-proxywasm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [ docker ] #, docker_swarm, kubernetes]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.24
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Download Proxywasm artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: proxywasm-plugin-wasm
|
||||
path: ./plugins/proxywasm
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/proxywasm/e2e/apacheapisix/${{ matrix.provider }} && bash ./run.sh
|
||||
|
||||
proxywasm_envoy_e2e:
|
||||
name: Run Sablier E2E tests for Proxywasm middleware on Envoy
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
- build-proxywasm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [ docker ] #, docker_swarm, kubernetes]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Download Proxywasm artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: proxywasm-plugin-wasm
|
||||
path: ./plugins/proxywasm
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/proxywasm/e2e/envoy/${{ matrix.provider }} && bash ./run.sh
|
||||
|
||||
proxywasm_nginx_e2e:
|
||||
name: Run Sablier E2E tests for Proxywasm middleware on Nginx
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
- build-proxywasm
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
provider: [ docker ] #, docker_swarm, kubernetes]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go 1.22
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: sablier-image-tar
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load --input /tmp/sablier.tar
|
||||
|
||||
- name: Download Proxywasm artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: proxywasm-plugin-wasm
|
||||
path: ./plugins/proxywasm
|
||||
|
||||
- name: Test ${{ matrix.provider }}
|
||||
run: cd plugins/proxywasm/e2e/nginx/${{ matrix.provider }} && bash ./run.sh
|
||||
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -1,34 +0,0 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Go 1.24
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.24
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
run: |
|
||||
npm i -G semantic-release@~23.0.0 @semantic-release/exec@~6.0.3 @semantic-release/git@~10.0.1
|
||||
npx semantic-release
|
||||
28
.traefik.yml
28
.traefik.yml
@@ -1,28 +0,0 @@
|
||||
displayName: Sablier
|
||||
type: middleware
|
||||
iconPath: ./docs/assets/img/icon.png
|
||||
bannerPath: ./docs/assets/img/banner.png
|
||||
|
||||
import: github.com/sablierapp/sablier/plugins/traefik
|
||||
|
||||
summary: "Start your containers on demand, shut them down automatically when there's no activity. Docker, Docker Swarm Mode and Kubernetes compatible."
|
||||
|
||||
testData:
|
||||
sablierUrl: http://sablier:10000 # The sablier URL service, must be reachable from the Traefik instance
|
||||
names: whoami,nginx # Comma separated names of containers/services/deployments etc.
|
||||
group: default # Group name to use to filter by label, ignored if names is set
|
||||
sessionDuration: 1m # The session duration after which containers/services/deployments instances are shutdown
|
||||
# You can only use one strategy at a time
|
||||
# To do so, only declare `dynamic` or `blocking`
|
||||
|
||||
# Dynamic strategy, provides the waiting webui
|
||||
dynamic:
|
||||
displayName: My Title # (Optional) Defaults to the middleware name
|
||||
showDetails: true # (Optional) Set to true or false to show details specifcally for this middleware, unset to use Sablier server defaults
|
||||
theme: hacker-terminal # (Optional) The theme to use
|
||||
refreshFrequency: 5s # (Optional) The loading page refresh frequency
|
||||
|
||||
# Blocking strategy, waits until services are up and running
|
||||
# but will not wait more than `timeout`
|
||||
# blocking:
|
||||
# timeout: 1m
|
||||
61
Makefile
61
Makefile
@@ -29,34 +29,12 @@ build:
|
||||
test:
|
||||
go test -tags="nomsgpack,remote,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
|
||||
|
||||
plugins: build-plugin-traefik test-plugin-traefik build-plugin-caddy test-plugin-caddy
|
||||
|
||||
build-plugin-traefik:
|
||||
cd plugins/traefik && go build -v .
|
||||
|
||||
test-plugin-traefik:
|
||||
cd plugins/traefik && go test -v ./...
|
||||
|
||||
build-plugin-caddy:
|
||||
cd plugins/caddy && go build -v .
|
||||
|
||||
test-plugin-caddy:
|
||||
cd plugins/caddy && go test -v .
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build --build-arg BUILDTIME=$(BUILDTIME) --build-arg VERSION=$(VERSION) --build-arg REVISION=$(GIT_REVISION) -t sablierapp/sablier:local .
|
||||
|
||||
caddy:
|
||||
docker build -t caddy:local plugins/caddy
|
||||
|
||||
release: $(PLATFORMS)
|
||||
|
||||
proxywasm:
|
||||
go generate ./plugins/proxywasm
|
||||
env GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o ./plugins/proxywasm/sablierproxywasm.wasm ./plugins/proxywasm
|
||||
cp ./plugins/proxywasm/sablierproxywasm.wasm ./sablierproxywasm_$(VERSION).wasm
|
||||
|
||||
.PHONY: release $(PLATFORMS)
|
||||
|
||||
LAST = 0.0.0
|
||||
@@ -73,42 +51,3 @@ update-doc-version-middleware:
|
||||
.PHONY: docs
|
||||
docs:
|
||||
npx --yes docsify-cli serve docs
|
||||
|
||||
# End to end tests
|
||||
e2e: e2e-caddy e2e-nginx e2e-traefik
|
||||
|
||||
## Caddy
|
||||
e2e-caddy-docker:
|
||||
cd plugins/caddy/e2e/docker && bash ./run.sh
|
||||
|
||||
e2e-caddy-swarm:
|
||||
cd plugins/caddy/e2e/docker_swarm && bash ./run.sh
|
||||
|
||||
# e2e-caddy-kubernetes:
|
||||
# cd plugins/caddy/e2e/kubernetes && bash ./run.sh
|
||||
|
||||
e2e-caddy: e2e-caddy-docker e2e-caddy-swarm # e2e-caddy-kubernetes
|
||||
|
||||
## NGinx
|
||||
e2e-nginx-docker:
|
||||
cd plugins/nginx/e2e/docker && bash ./run.sh
|
||||
|
||||
e2e-nginx-swarm:
|
||||
cd plugins/nginx/e2e/docker_swarm && bash ./run.sh
|
||||
|
||||
e2e-nginx-kubernetes:
|
||||
cd plugins/nginx/e2e/kubernetes && bash ./run.sh
|
||||
|
||||
e2e-nginx: e2e-nginx-docker e2e-nginx-swarm e2e-nginx-kubernetes
|
||||
|
||||
## Traefik
|
||||
e2e-traefik-docker:
|
||||
cd plugins/traefik/e2e/docker && bash ./run.sh
|
||||
|
||||
e2e-traefik-swarm:
|
||||
cd plugins/traefik/e2e/docker_swarm && bash ./run.sh
|
||||
|
||||
e2e-traefik-kubernetes:
|
||||
cd plugins/traefik/e2e/kubernetes && bash ./run.sh
|
||||
|
||||
e2e-traefik: e2e-traefik-docker e2e-traefik-swarm e2e-traefik-kubernetes
|
||||
|
||||
125
e2e/e2e_test.go
125
e2e/e2e_test.go
@@ -1,125 +0,0 @@
|
||||
//go:build e2e
|
||||
// +build e2e
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gavv/httpexpect/v2"
|
||||
)
|
||||
|
||||
func Test_Dynamic(t *testing.T) {
|
||||
e := httpexpect.Default(t, "http://localhost:8080/dynamic/")
|
||||
|
||||
e.GET("/whoami").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Contains(`Dynamic Whoami`).
|
||||
Contains(`Your instance(s) will stop after 1 minute of inactivity`)
|
||||
|
||||
e.GET("/whoami").
|
||||
WithMaxRetries(20).
|
||||
WithRetryDelay(50*time.Millisecond, time.Second*2).
|
||||
WithRetryPolicyFunc(RetryUntilBodyContains("Host: localhost:8080")).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().Contains(`Host: localhost:8080`)
|
||||
}
|
||||
|
||||
func Test_Blocking(t *testing.T) {
|
||||
e := httpexpect.Default(t, "http://localhost:8080/blocking/")
|
||||
|
||||
e.GET("/whoami").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().Contains(`Host: localhost:8080`)
|
||||
}
|
||||
|
||||
func Test_Multiple(t *testing.T) {
|
||||
e := httpexpect.Default(t, "http://localhost:8080/multiple/")
|
||||
|
||||
e.GET("/whoami").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Contains(`Multiple Whoami`).
|
||||
Contains(`Your instance(s) will stop after 1 minute of inactivity`)
|
||||
|
||||
e.GET("/whoami").
|
||||
WithMaxRetries(20).
|
||||
WithRetryDelay(50*time.Millisecond, time.Second*2).
|
||||
WithRetryPolicyFunc(RetryUntilBodyContains("Host: localhost:8080")).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().Contains(`Host: localhost:8080`)
|
||||
|
||||
e.GET("/nginx").
|
||||
WithMaxRetries(20).
|
||||
WithRetryDelay(50*time.Millisecond, time.Second*2).
|
||||
WithRetryPolicyFunc(RetryUntilBodyContains("nginx/")).
|
||||
Expect().
|
||||
Status(http.StatusNotFound).
|
||||
Body().Contains(`nginx/`)
|
||||
}
|
||||
|
||||
func Test_Healthy(t *testing.T) {
|
||||
e := httpexpect.Default(t, "http://localhost:8080/healthy/")
|
||||
|
||||
e.GET("/nginx").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Contains(`Healthy Nginx`).
|
||||
Contains(`Your instance(s) will stop after 1 minute of inactivity`)
|
||||
|
||||
e.GET("/nginx").
|
||||
WithMaxRetries(20).
|
||||
WithRetryDelay(50*time.Millisecond, time.Second*2).
|
||||
WithRetryPolicyFunc(RetryUntilBodyContains("nginx/")).
|
||||
Expect().
|
||||
Status(http.StatusNotFound).
|
||||
Body().Contains(`nginx/`)
|
||||
}
|
||||
|
||||
func Test_Group(t *testing.T) {
|
||||
e := httpexpect.Default(t, "http://localhost:8080/")
|
||||
|
||||
e.GET("/group").
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().
|
||||
Contains(`Group E2E`).
|
||||
Contains(`Your instance(s) will stop after 1 minute of inactivity`)
|
||||
|
||||
e.GET("/group").
|
||||
WithMaxRetries(20).
|
||||
WithRetryDelay(50*time.Millisecond, time.Second*2).
|
||||
WithRetryPolicyFunc(RetryUntilBodyContains("Host: localhost:8080")).
|
||||
Expect().
|
||||
Status(http.StatusOK).
|
||||
Body().Contains(`Host: localhost:8080`)
|
||||
}
|
||||
|
||||
func RetryUntilBodyContains(contains string) func(resp *http.Response, err error) bool {
|
||||
return func(resp *http.Response, err error) bool {
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if resp.Body != nil {
|
||||
// Check body if available, etc.
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return !strings.Contains(string(body), contains)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
24
go.mod
24
go.mod
@@ -5,7 +5,6 @@ go 1.24.0
|
||||
require (
|
||||
github.com/containers/podman/v5 v5.6.2
|
||||
github.com/docker/docker v28.5.1+incompatible
|
||||
github.com/gavv/httpexpect/v2 v2.17.0
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/lmittmann/tint v1.1.2
|
||||
@@ -38,16 +37,13 @@ require (
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.13.0 // indirect
|
||||
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/Zxilly/go-size-analyzer v1.7.7 // indirect
|
||||
github.com/ZxillyFork/gore v0.0.0-20250212071411-56f1a74740b2 // indirect
|
||||
github.com/ZxillyFork/gosym v0.0.0-20240510024817-deed2b882525 // indirect
|
||||
github.com/ZxillyFork/trie v0.0.0-20240512061834-f75150731646 // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/alecthomas/kong v1.8.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/blacktop/go-dwarf v1.0.10 // indirect
|
||||
github.com/blacktop/go-macho v1.1.238 // indirect
|
||||
@@ -90,8 +86,6 @@ require (
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
@@ -110,7 +104,6 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
@@ -119,15 +112,12 @@ require (
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-containerregistry v0.20.3 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/schema v1.4.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hpcloud/tail v1.0.0 // indirect
|
||||
github.com/imkira/go-interpol v1.1.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.6 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
@@ -147,7 +137,6 @@ require (
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
@@ -155,7 +144,6 @@ require (
|
||||
github.com/mdelapenya/tlscert v0.2.0 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.1.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
@@ -175,6 +163,7 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nikolaydubina/treemap v1.2.5 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/onsi/ginkgo v1.10.1 // indirect
|
||||
github.com/opencontainers/cgroups v0.0.4 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
@@ -192,9 +181,7 @@ require (
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/sanity-io/litter v1.5.6 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
|
||||
github.com/sigstore/fulcio v1.6.6 // indirect
|
||||
github.com/sigstore/protobuf-specs v0.4.1 // indirect
|
||||
@@ -215,17 +202,9 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.58.0 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.10.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
|
||||
github.com/yudai/gojsondiff v1.0.0 // indirect
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
@@ -258,7 +237,6 @@ require (
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
moul.io/http2curl/v2 v2.3.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
|
||||
61
go.sum
61
go.sum
@@ -12,8 +12,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
|
||||
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
|
||||
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
|
||||
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/Zxilly/go-size-analyzer v1.7.7 h1:z+OwZgSENyR5EYh2Oce3qvDgC/ghEIwL2cf86ilKkJA=
|
||||
@@ -26,16 +24,12 @@ github.com/ZxillyFork/trie v0.0.0-20240512061834-f75150731646 h1:Mkm17zXN+CiAXdV
|
||||
github.com/ZxillyFork/trie v0.0.0-20240512061834-f75150731646/go.mod h1:wbU6+GbSHDwVSMpzFyOVrX/WdFHXH4j7j8Rg8rbyo+4=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/kong v1.8.1 h1:6aamvWBE/REnR/BCq10EcozmcpUPc5aGI1lPAWdB0EE=
|
||||
github.com/alecthomas/kong v1.8.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||
@@ -127,7 +121,6 @@ github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
||||
github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48=
|
||||
github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -162,10 +155,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
@@ -177,8 +166,6 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gavv/httpexpect/v2 v2.17.0 h1:nIJqt5v5e4P7/0jODpX2gtSw+pHXUqdP28YcjqwDZmE=
|
||||
github.com/gavv/httpexpect/v2 v2.17.0/go.mod h1:E8ENFlT9MZ3Si2sfM6c6ONdwXV2noBCGkhA+lkJgkP0=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
@@ -217,8 +204,6 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -249,7 +234,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@@ -259,8 +243,6 @@ github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM
|
||||
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
|
||||
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
|
||||
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
@@ -272,8 +254,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -283,12 +263,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8=
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.6 h1:LyezkL+1SuqH2z47e5IMQkYUIcs2BD+MnpdPRiRcN0c=
|
||||
@@ -319,11 +295,8 @@ github.com/knadh/profiler v0.2.0/go.mod h1:LqNkAu++MfFkbEDA63AmRaIf6UkGrLXyZ5VQQ
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
@@ -342,8 +315,6 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
@@ -359,8 +330,6 @@ github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
@@ -431,12 +400,10 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
|
||||
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -466,15 +433,12 @@ github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/samber/slog-gin v1.17.2 h1:eKi0x9brNl7vwLl3+9Zuk2ZiIsneHd55/R01TqV9bM8=
|
||||
github.com/samber/slog-gin v1.17.2/go.mod h1:7R4VMQGENllRLLnwGyoB5nUSB+qzxThpGe5G02xla6o=
|
||||
github.com/sanity-io/litter v1.5.6 h1:hCFycYzhRnW4niFbbmR7QKdmds69PbVa/sNmEN5euSU=
|
||||
github.com/sanity-io/litter v1.5.6/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
|
||||
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
@@ -511,9 +475,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
@@ -526,7 +488,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI=
|
||||
github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI=
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/testcontainers/testcontainers-go v0.39.0 h1:uCUJ5tA+fcxbFAB0uP3pIK3EJ2IjjDUHFSZ1H1UxAts=
|
||||
@@ -553,33 +514,18 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/valkey-io/valkey-go v1.0.67 h1:QPaRcuBmazhyoWTxk7I2XcSALhoL7UhAReR5o/rh1Po=
|
||||
github.com/valkey-io/valkey-go v1.0.67/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE=
|
||||
github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vbauerster/mpb/v8 v8.10.2 h1:2uBykSHAYHekE11YvJhKxYmLATKHAGorZwFlyNw4hHM=
|
||||
github.com/vbauerster/mpb/v8 v8.10.2/go.mod h1:+Ja4P92E3/CorSZgfDtK46D7AVbDqmBQRTmyTqPElo0=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
@@ -638,7 +584,6 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
@@ -737,7 +682,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
@@ -777,7 +721,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
@@ -788,8 +731,6 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -809,8 +750,6 @@ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOP
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
|
||||
10
go.work
10
go.work
@@ -1,10 +0,0 @@
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.0
|
||||
|
||||
use (
|
||||
.
|
||||
./plugins/caddy
|
||||
./plugins/proxywasm
|
||||
./plugins/traefik
|
||||
)
|
||||
3491
go.work.sum
3491
go.work.sum
File diff suppressed because it is too large
Load Diff
@@ -5,11 +5,25 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/containers/podman/v5/libpod/define"
|
||||
"github.com/containers/podman/v5/pkg/bindings/containers"
|
||||
)
|
||||
|
||||
func (p *Provider) InstanceStart(ctx context.Context, name string) error {
|
||||
p.l.DebugContext(ctx, "starting container", "name", name)
|
||||
spec, err := containers.Inspect(p.conn, name, nil)
|
||||
if err != nil {
|
||||
p.l.ErrorContext(ctx, "cannot inspect container after starting", slog.String("name", name), slog.Any("error", err))
|
||||
return fmt.Errorf("cannot inspect container %s after starting: %w", name, err)
|
||||
}
|
||||
|
||||
status, err := define.StringToContainerStatus(spec.State.Status)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot convert container status: %w", err)
|
||||
}
|
||||
|
||||
if status == define.ContainerStateRunning {
|
||||
|
||||
}
|
||||
|
||||
// TODO: Create a context from the ctx argument with the p.conn
|
||||
err := containers.Start(p.conn, name, nil)
|
||||
@@ -17,5 +31,6 @@ func (p *Provider) InstanceStart(ctx context.Context, name string) error {
|
||||
p.l.ErrorContext(ctx, "cannot start container", slog.String("name", name), slog.Any("error", err))
|
||||
return fmt.Errorf("cannot start container %s: %w", name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
16
plugins/caddy/.gitignore
vendored
16
plugins/caddy/.gitignore
vendored
@@ -1,16 +0,0 @@
|
||||
# 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
|
||||
@@ -1,11 +0,0 @@
|
||||
ARG CADDY_VERSION=2.10.0
|
||||
FROM caddy:${CADDY_VERSION}-builder AS builder
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/sablierapp/sablier/plugins/caddy=.
|
||||
|
||||
FROM caddy:${CADDY_VERSION}
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
@@ -1,92 +0,0 @@
|
||||
> [!IMPORTANT]
|
||||
> This plugin now has its own reepository: https://github.com/sablierapp/sablier-caddy-plugin
|
||||
>
|
||||
> This plugin will no longer receive updates.
|
||||
|
||||
# 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
|
||||
|
||||
```bash
|
||||
docker build https://github.com/sablierapp/sablier.git#v1.10.1:plugins/caddy -t caddy:with-sablier
|
||||
```
|
||||
|
||||
**Note:** You can change `main` for any other branch (such as `beta`, or tags `v1.10.1`)
|
||||
|
||||
### By updating your Caddy Dockerfile
|
||||
|
||||
```Dockerfile
|
||||
ARG CADDY_VERSION=2.9.1
|
||||
FROM caddy:${CADDY_VERSION}-builder AS builder
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/sablierapp/sablier/plugins/caddy
|
||||
|
||||
FROM caddy:${CADDY_VERSION}
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You can have the following configuration:
|
||||
|
||||
```Caddyfile
|
||||
: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.
|
||||
|
||||
```Caddyfile
|
||||
: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 sablierapp/sablier:local ../..`
|
||||
3. Run test
|
||||
`cd e2e/docker && bash ./run.sh`
|
||||
@@ -1,278 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,450 +0,0 @@
|
||||
package caddy_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/sablierapp/sablier/plugins/caddy"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
: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
|
||||
}
|
||||
|
||||
route /group {
|
||||
sablier http://sablier:10000 {
|
||||
group E2E
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Group E2E
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
proxy:
|
||||
image: caddy:local
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
restart: "no"
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/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/sablierapp/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
|
||||
run_docker_classic_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,72 +0,0 @@
|
||||
: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
|
||||
}
|
||||
|
||||
route /group {
|
||||
sablier http://sablier:10000 {
|
||||
group E2E
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Group E2E
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=swarm
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
deploy:
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
replicas: 0
|
||||
|
||||
nginx:
|
||||
image: nginx:1.23.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
deploy:
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
replicas: 0
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/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/sablierapp/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
|
||||
run_docker_swarm_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,34 +0,0 @@
|
||||
: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
|
||||
}
|
||||
|
||||
route /group {
|
||||
sablier url=http://tasks.sablier:10000 {
|
||||
group E2E
|
||||
session_duration 1m
|
||||
dynamic {
|
||||
display_name Group E2E
|
||||
theme hacker-terminal
|
||||
}
|
||||
}
|
||||
reverse_proxy whoami:80
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
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/sablierapp/sablier'
|
||||
ports:
|
||||
- 6443:6443 # Kubernetes API Server
|
||||
- 8080:80 # Ingress controller port 80
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/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 sablierapp/sablier:local into k3s..."
|
||||
docker save sablierapp/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/sablierapp/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
|
||||
@@ -1,25 +0,0 @@
|
||||
# traefik helm values
|
||||
image:
|
||||
tag: "2.9.1"
|
||||
|
||||
additionalArguments:
|
||||
- "--experimental.localPlugins.sablier.moduleName=github.com/sablierapp/sablier"
|
||||
|
||||
providers:
|
||||
kubernetesIngress:
|
||||
allowEmptyServices: true
|
||||
kubernetesCRD:
|
||||
allowEmptyServices: true
|
||||
|
||||
additionalVolumeMounts:
|
||||
- name: local-sablier-plugin
|
||||
mountPath: /plugins-local/src/github.com/sablierapp/sablier
|
||||
|
||||
deployment:
|
||||
additionalVolumes:
|
||||
- name: local-sablier-plugin
|
||||
hostPath:
|
||||
# directory location on host
|
||||
path: /plugins-local/src/github.com/sablierapp/sablier
|
||||
# this field is optional
|
||||
type: Directory
|
||||
@@ -1,121 +0,0 @@
|
||||
module github.com/sablierapp/sablier/plugins/caddy
|
||||
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require github.com/caddyserver/caddy/v2 v2.10.0
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/KimMachineGun/automemlimit v0.7.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/caddyserver/certmagic v0.23.0 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // 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.2.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/cel-go v0.24.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/libdns/libdns v1.0.0-beta.1 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.2 // indirect
|
||||
github.com/miekg/dns v1.1.63 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.50.1 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.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.26.1 // indirect
|
||||
github.com/smallstep/nosql v0.6.1 // indirect
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
||||
github.com/smallstep/truststore v0.13.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 // indirect
|
||||
github.com/urfave/cli v1.22.14 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.3.9 // indirect
|
||||
go.step.sm/cli-utils v0.9.0 // indirect
|
||||
go.step.sm/crypto v0.45.0 // indirect
|
||||
go.step.sm/linkedca v0.20.1 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20250305170421-49bf5b80c810 // indirect
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
||||
@@ -1,732 +0,0 @@
|
||||
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
|
||||
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
|
||||
cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
|
||||
cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
|
||||
cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE=
|
||||
cloud.google.com/go/kms v1.16.0 h1:1yZsRPhmargZOmY+fVAh8IKiR9HzCb0U1zsxb5g2nRY=
|
||||
cloud.google.com/go/kms v1.16.0/go.mod h1:olQUXy2Xud+1GzYfiBO9N0RhjsJk5IJLU6n/ethLXVc=
|
||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/KimMachineGun/automemlimit v0.7.1 h1:QcG/0iCOLChjfUweIMC3YL5Xy9C3VBeNmCZHrZfJMBw=
|
||||
github.com/KimMachineGun/automemlimit v0.7.1/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.13 h1:WbKW8hOzrWoOA/+35S5okqO/2Ap8hkkFUzoW8Hzq24A=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.13/go.mod h1:XLiyiTMnguytjRER7u5RIkhIqS8Nyz41SwAWb4xEjxs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.13 h1:XDCJDzk/u5cN7Aple7D/MiAhx1Rjo/0nueJ0La8mRuE=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.13/go.mod h1:FMNcjQrmuBYvOTZDtOLCIu0esmxjF7RuA/89iSXWzQI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1 h1:5wtyAwuUiJiM3DHYeGZmP5iMonM7DFBWAEaaVPHYZA0=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 h1:o5cTaeunSpfXiLTIBx5xo2enQmiChtu1IBbzXnfU9Hs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 h1:Qe0r0lVURDDeBQJ4yP+BOrJkvkiCo/3FH/t+wY11dmw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1qYpP5uU7cvNql8ns=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
|
||||
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
|
||||
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/caddyserver/caddy/v2 v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=
|
||||
github.com/caddyserver/caddy/v2 v2.10.0/go.mod h1:q+dgBS3xtIJJGYI2H5Nyh9+4BvhQQ9yCGmECv4Ubdjo=
|
||||
github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU=
|
||||
github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4=
|
||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
|
||||
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
||||
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI=
|
||||
github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8=
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
|
||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
|
||||
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
|
||||
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
|
||||
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
|
||||
github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
||||
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
|
||||
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ=
|
||||
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
|
||||
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
|
||||
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/slackhq/nebula v1.6.1 h1:/OCTR3abj0Sbf2nGoLUrdDXImrCv0ZVFpVPP5qa0DsM=
|
||||
github.com/slackhq/nebula v1.6.1/go.mod h1:UmkqnXe4O53QwToSl/gG7sM4BroQwAB7dd4hUaT6MlI=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
|
||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
||||
github.com/smallstep/certificates v0.26.1 h1:FIUliEBcExSfJJDhRFA/s8aZgMIFuorexnRSKQd884o=
|
||||
github.com/smallstep/certificates v0.26.1/go.mod h1:OQMrW39IrGKDViKSHrKcgSQArMZ8c7EcjhYKK7mYqis=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
|
||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
|
||||
github.com/smallstep/nosql v0.6.1 h1:X8IBZFTRIp1gmuf23ne/jlD/BWKJtDQbtatxEn7Et1Y=
|
||||
github.com/smallstep/nosql v0.6.1/go.mod h1:vrN+CftYYNnDM+DQqd863ATynvYFm/6FuY9D4TeAm2Y=
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 h1:B6cED3iLJTgxpdh4tuqByDjRRKan2EvtnOfHr2zHJVg=
|
||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y=
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw=
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
|
||||
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
|
||||
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 h1:uxMgm0C+EjytfAqyfBG55ZONKQ7mvd7x4YYCWsf8QHQ=
|
||||
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.step.sm/cli-utils v0.9.0 h1:55jYcsQbnArNqepZyAwcato6Zy2MoZDRkWW+jF+aPfQ=
|
||||
go.step.sm/cli-utils v0.9.0/go.mod h1:Y/CRoWl1FVR9j+7PnAewufAwKmBOTzR6l9+7EYGAnp8=
|
||||
go.step.sm/crypto v0.45.0 h1:Z0WYAaaOYrJmKP9sJkPW+6wy3pgN3Ija8ek/D4serjc=
|
||||
go.step.sm/crypto v0.45.0/go.mod h1:6IYlT0L2jfj81nVyCPpvA5cORy0EVHPhieSgQyuwHIY=
|
||||
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
||||
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20250305170421-49bf5b80c810 h1:V5+zy0jmgNYmK1uW/sPpBw8ioFvalrhaUrYWmu1Fpe4=
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20250305170421-49bf5b80c810/go.mod h1:lxN5T34bK4Z/i6cMaU7frUU57VkDXFD4Kamfl/cp9oU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
|
||||
google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw=
|
||||
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
@@ -1,73 +0,0 @@
|
||||
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)
|
||||
)
|
||||
@@ -1,108 +0,0 @@
|
||||
package caddy_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
plugin "github.com/sablierapp/sablier/plugins/caddy"
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
ARG CADDY_VERSION=2.10.0
|
||||
FROM caddy:${CADDY_VERSION}-builder AS builder
|
||||
|
||||
RUN xcaddy build \
|
||||
--with github.com/sablierapp/sablier/plugins/caddy
|
||||
|
||||
FROM caddy:${CADDY_VERSION}
|
||||
|
||||
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||
@@ -1,91 +0,0 @@
|
||||
# Nginx NJS Sablier Module
|
||||
|
||||
Nginx NJS Sablier Module
|
||||
|
||||
**Kubernetes is not supported yet**
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Load the `ngx_http_js_module.so` in the main nginx config file `/etc/nginx/nginx.conf`
|
||||
```
|
||||
load_module modules/ngx_http_js_module.so;
|
||||
```
|
||||
2. Copy/volume the `sablier.js` file to `/etc/nginx/conf.d/sablier.js`
|
||||
3. Use this sample for your APIs
|
||||
```nginx
|
||||
js_import conf.d/sablier.js;
|
||||
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
subrequest_output_buffer_size 32k;
|
||||
|
||||
# The internal location to reach sablier API
|
||||
set $sablierUrl /sablier;
|
||||
# Shared variable for default session duration
|
||||
set $sablierSessionDuration 1m;
|
||||
|
||||
# internal location for sablier middleware
|
||||
# here, the sablier API is a container named "sablier" inside the same network as nginx
|
||||
location /sablier/ {
|
||||
internal;
|
||||
proxy_method GET;
|
||||
proxy_pass http://sablier:10000/;
|
||||
}
|
||||
|
||||
# A named location that can be used by the sablier middleware to redirect
|
||||
location @whoami {
|
||||
# Here is your container name, same in
|
||||
# set $sablierNames whoami;
|
||||
# Use variable in order to refresh DNS cache
|
||||
set $whoami_server whoami;
|
||||
proxy_pass http://$whoami_server:80;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# The actual location to match your API
|
||||
# Will answer by a waiting page or redirect to your app if
|
||||
# it is already up
|
||||
location /dynamic/whoami {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
# Here is your container name
|
||||
set $sablierNames whoami;
|
||||
set $sablierDynamicName "Dynamic Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Available variables
|
||||
|
||||
You can configure the middleware behavior with the following variables:
|
||||
|
||||
**General Configuration**
|
||||
|
||||
- `set $sablierUrl` The internal routing to reach Sablier API
|
||||
- `set $sablierNames` Comma separated names of containers/services/deployments etc.
|
||||
- `set $sablierGroup` Group name to use to filter by label, ignored if sablierNames is set
|
||||
- `set $sablierSessionDuration` The session duration after which containers/services/deployments instances are shutdown
|
||||
- `set $sablierNginxInternalRedirect` The internal location for the service to redirect e.g. @nginx
|
||||
|
||||
**Dynamic Configuration**
|
||||
|
||||
*if any of these variables is set, then all Blocking Configuration is ignored*
|
||||
|
||||
- `set $sablierDynamicName`
|
||||
- `set $sablierDynamicShowDetails` Set to true or false to show details specifcally for this middleware, unset to use Sablier server defaults
|
||||
- `set $sablierDynamicTheme` The theme to use
|
||||
- `set $sablierDynamicRefreshFrequency` The loading page refresh frequency
|
||||
|
||||
**Blocking Configuration**
|
||||
|
||||
- `set $sablierBlockingTimeout` waits until services are up and running but will not wait more than `timeout`
|
||||
|
||||
## Development
|
||||
|
||||
Change the `njs/sablier.js` configuration and start the tests for the given provider `e2e/<provider>.sh` (docker, kubernetes, etc.)
|
||||
@@ -1,41 +0,0 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
proxy:
|
||||
image: nginx:1.27.1
|
||||
ports:
|
||||
- 8080:80
|
||||
volumes:
|
||||
# Used to load js module
|
||||
- ../nginx.conf:/etc/nginx/nginx.conf
|
||||
- ../../njs/sablier.js:/etc/nginx/conf.d/sablier.js
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
ports:
|
||||
- 10000:10000
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
@@ -1,92 +0,0 @@
|
||||
js_import conf.d/sablier.js;
|
||||
|
||||
# internal docker resolver, see /etc/resolv.conf on proxy container
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
subrequest_output_buffer_size 32k;
|
||||
|
||||
set $sablierUrl /sablier;
|
||||
set $sablierSessionDuration 1m;
|
||||
|
||||
location @whoami {
|
||||
# Use variable in order to refresh DNS cache
|
||||
set $whoami_server whoami;
|
||||
proxy_pass http://$whoami_server:80;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location @nginx {
|
||||
# Use variable in order to refresh DNS cache
|
||||
set $nginx_server nginx;
|
||||
proxy_pass http://$nginx_server:80;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location /sablier/ {
|
||||
internal;
|
||||
proxy_method GET;
|
||||
proxy_pass http://sablier:10000/;
|
||||
}
|
||||
|
||||
location /dynamic/whoami {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierNames docker_classic_e2e-whoami-1;
|
||||
set $sablierDynamicName "Dynamic Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /blocking/whoami {
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierNames docker_classic_e2e-whoami-1;
|
||||
set $sablierBlockingTimeout 30s;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /multiple/nginx {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @nginx;
|
||||
set $sablierNames docker_classic_e2e-nginx-1,docker_classic_e2e-whoami-1;
|
||||
set $sablierDynamicName "Multiple Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /multiple/whoami {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierNames docker_classic_e2e-nginx-1,docker_classic_e2e-whoami-1;
|
||||
set $sablierDynamicName "Multiple Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /healthy/nginx {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @nginx;
|
||||
set $sablierNames docker_classic_e2e-nginx-1;
|
||||
set $sablierDynamicName "Healthy Nginx";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /group {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierGroup E2E;
|
||||
set $sablierDynamicName "Group E2E";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/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
|
||||
}
|
||||
|
||||
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/sablierapp/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
|
||||
run_docker_classic_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,48 +0,0 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
proxy:
|
||||
image: nginx:1.23.1
|
||||
ports:
|
||||
- target: 80
|
||||
published: 8080
|
||||
protocol: tcp
|
||||
mode: host # Won't work in github actions otherwise
|
||||
volumes:
|
||||
# Used to load js module
|
||||
- ../nginx.conf:/etc/nginx/nginx.conf
|
||||
- ../../njs/sablier.js:/etc/nginx/conf.d/sablier.js
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
ports:
|
||||
- 10000:10000
|
||||
command:
|
||||
- start
|
||||
- --provider.name=swarm
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
deploy:
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
replicas: 0
|
||||
|
||||
nginx:
|
||||
image: nginx:1.23.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
deploy:
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
replicas: 0
|
||||
@@ -1,92 +0,0 @@
|
||||
js_import conf.d/sablier.js;
|
||||
|
||||
# internal docker resolver, see /etc/resolv.conf on proxy container
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
subrequest_output_buffer_size 32k;
|
||||
|
||||
set $sablierUrl /sablier;
|
||||
set $sablierSessionDuration 1m;
|
||||
|
||||
location @whoami {
|
||||
# Use variable in order to refresh DNS cache
|
||||
set $whoami_server whoami;
|
||||
proxy_pass http://$whoami_server:80;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location @nginx {
|
||||
# Use variable in order to refresh DNS cache
|
||||
set $nginx_server nginx;
|
||||
proxy_pass http://$nginx_server:80;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location /sablier/ {
|
||||
internal;
|
||||
proxy_method GET;
|
||||
proxy_pass http://sablier:10000/;
|
||||
}
|
||||
|
||||
location /dynamic/whoami {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierNames DOCKER_SWARM_E2E_whoami;
|
||||
set $sablierDynamicName "Dynamic Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /blocking/whoami {
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierNames DOCKER_SWARM_E2E_whoami;
|
||||
set $sablierBlockingTimeout 30s;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /multiple/nginx {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @nginx;
|
||||
set $sablierNames DOCKER_SWARM_E2E_nginx,DOCKER_SWARM_E2E_whoami;
|
||||
set $sablierDynamicName "Multiple Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /multiple/whoami {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierNames DOCKER_SWARM_E2E_nginx,DOCKER_SWARM_E2E_whoami;
|
||||
set $sablierDynamicName "Multiple Whoami";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /healthy/nginx {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @nginx;
|
||||
set $sablierNames DOCKER_SWARM_E2E_nginx;
|
||||
set $sablierDynamicName "Healthy Nginx";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
|
||||
location /group {
|
||||
set $sablierDynamicShowDetails true;
|
||||
set $sablierDynamicRefreshFrequency 5s;
|
||||
set $sablierNginxInternalRedirect @whoami;
|
||||
set $sablierGroup E2E;
|
||||
set $sablierDynamicName "Group E2E";
|
||||
set $sablierDynamicTheme hacker-terminal;
|
||||
js_content sablier.call;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/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/sablierapp/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
|
||||
run_docker_swarm_test Test_Dynamic
|
||||
run_docker_swarm_test Test_Blocking
|
||||
run_docker_swarm_test Test_Multiple
|
||||
run_docker_swarm_test Test_Healthy
|
||||
run_docker_swarm_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,25 +0,0 @@
|
||||
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/sablierapp/sablier'
|
||||
ports:
|
||||
- 6443:6443 # Kubernetes API Server
|
||||
- 8080:80 # Ingress controller port 80
|
||||
@@ -1,124 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami-deployment
|
||||
labels:
|
||||
app: whoami
|
||||
sablier.enable: true
|
||||
sablier.group: E2E
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-service
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
sablier.enable: true
|
||||
sablier.group: E2E
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.29.3
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-multiple-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /multiple/nginx
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-healthy-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /healthy/nginx
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: group-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /group
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
@@ -1,78 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sablier-deployment
|
||||
namespace: kube-system
|
||||
labels:
|
||||
app: sablier
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sablier
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sablier
|
||||
spec:
|
||||
serviceAccountName: sablier
|
||||
containers:
|
||||
- name: sablier
|
||||
image: sablierapp/sablier:local
|
||||
args: ["start", "--provider.name=kubernetes", "--logging.level=debug"]
|
||||
ports:
|
||||
- containerPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
app: sablier
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 10000
|
||||
targetPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
- ""
|
||||
resources:
|
||||
- deployments
|
||||
- deployments/scale
|
||||
- statefulsets
|
||||
- statefulsets/scale
|
||||
verbs:
|
||||
- patch # Scale up and down
|
||||
- get # Retrieve info about specific dep
|
||||
- update # Scale up and down
|
||||
- list # Events
|
||||
- watch # Events
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: sablier
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/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 sablierapp/sablier:local into k3s..."
|
||||
docker save sablierapp/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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
kubectl -n kube-system logs deployments/sablier-deployment
|
||||
# kubectl -n kube-system logs deployments/nginx
|
||||
fi
|
||||
|
||||
destroy_deployment
|
||||
}
|
||||
|
||||
trap destroy_kubernetes EXIT
|
||||
|
||||
prepare_kubernetes
|
||||
prepare_nginx # 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
|
||||
@@ -1,33 +0,0 @@
|
||||
load_module modules/ngx_http_js_module.so;
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
const querystring = require('querystring')
|
||||
|
||||
function call(r) {
|
||||
|
||||
const config = createConfigurationFromVariables(r)
|
||||
const request = buildRequest(config)
|
||||
|
||||
r.subrequest(request.url, request.query,
|
||||
function(reply) {
|
||||
if (reply.headersOut["X-Sablier-Session-Status"] == "ready") {
|
||||
r.internalRedirect(config.internalRedirect);
|
||||
} else {
|
||||
r.headersOut["Content-Type"] = reply.headersOut["Content-Type"]
|
||||
r.headersOut["Content-Length"] = reply.headersOut["Content-Length"]
|
||||
r.return(200, reply.responseBuffer);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SablierConfig
|
||||
* @property {string} sablierUrl
|
||||
* @property {string} names
|
||||
* @property {string} group
|
||||
* @property {string} sessionDuration
|
||||
* @property {string} internalRedirect
|
||||
* @property {string} displayName
|
||||
* @property {string} showDetails
|
||||
* @property {string} theme
|
||||
* @property {string} refreshFrequency
|
||||
* @property {string} timeout
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} headers
|
||||
* @returns {SablierConfig}
|
||||
*/
|
||||
function createConfigurationFromVariables(r) {
|
||||
return {
|
||||
sablierUrl: r.variables.sablierUrl,
|
||||
names: r.variables.sablierNames,
|
||||
group: r.variables.sablierGroup,
|
||||
sessionDuration: r.variables.sablierSessionDuration,
|
||||
internalRedirect: r.variables.sablierNginxInternalRedirect,
|
||||
|
||||
displayName: r.variables.sablierDynamicName,
|
||||
showDetails: r.variables.sablierDynamicShowDetails,
|
||||
theme: r.variables.sablierDynamicTheme,
|
||||
refreshFrequency: r.variables.sablierDynamicRefreshFrequency,
|
||||
|
||||
timeout: r.variables.sablierBlockingTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SablierConfig} c
|
||||
* @returns
|
||||
*/
|
||||
function buildRequest(c) {
|
||||
if (c.timeout == undefined || c.timeout == "") {
|
||||
return createDynamicUrl(c)
|
||||
} else {
|
||||
return createBlockingUrl(c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SablierConfig} config
|
||||
* @returns
|
||||
*/
|
||||
function createDynamicUrl(config) {
|
||||
const url = `${config.sablierUrl}/api/strategies/dynamic`
|
||||
const query = {
|
||||
session_duration: config.sessionDuration,
|
||||
display_name:config.displayName,
|
||||
theme: config.theme,
|
||||
refresh_frequency: config.refreshFrequency,
|
||||
show_details: config.showDetails
|
||||
};
|
||||
|
||||
if(config.names) {
|
||||
query.names = config.names.split(",").map(name => name.trim())
|
||||
} else if(config.group) {
|
||||
query.group = config.group
|
||||
} else {
|
||||
throw new Error('you must specify names or group');
|
||||
}
|
||||
|
||||
return {url, query: querystring.stringify(query)}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SablierConfig} config
|
||||
* @returns
|
||||
*/
|
||||
function createBlockingUrl(config) {
|
||||
const url = `${config.sablierUrl}/api/strategies/blocking`
|
||||
const query = {
|
||||
session_duration: config.sessionDuration,
|
||||
timeout:config.timeout,
|
||||
};
|
||||
|
||||
if(config.names) {
|
||||
query.names = config.names.split(",").map(name => name.trim())
|
||||
} else if(config.group) {
|
||||
query.group = config.group
|
||||
} else {
|
||||
throw new Error('you must specify names or group');
|
||||
}
|
||||
|
||||
return {url, query: querystring.stringify(query)}
|
||||
}
|
||||
|
||||
export default { call };
|
||||
@@ -1,44 +0,0 @@
|
||||
package main
|
||||
|
||||
import jsoniter "github.com/json-iterator/tinygo"
|
||||
|
||||
type BlockingConfiguration_json struct {
|
||||
}
|
||||
func (json BlockingConfiguration_json) Type() interface{} {
|
||||
var val BlockingConfiguration
|
||||
return val
|
||||
}
|
||||
func (json BlockingConfiguration_json) Unmarshal(iter *jsoniter.Iterator, out interface{}) {
|
||||
BlockingConfiguration_json_unmarshal(iter, out.(*BlockingConfiguration))
|
||||
}
|
||||
func (json BlockingConfiguration_json) Marshal(stream *jsoniter.Stream, val interface{}) {
|
||||
BlockingConfiguration_json_marshal(stream, val.(BlockingConfiguration))
|
||||
}
|
||||
func BlockingConfiguration_json_unmarshal(iter *jsoniter.Iterator, out *BlockingConfiguration) {
|
||||
more := iter.ReadObjectHead()
|
||||
for more {
|
||||
field := iter.ReadObjectField()
|
||||
if !BlockingConfiguration_json_unmarshal_field(iter, field, out) {
|
||||
iter.Skip()
|
||||
}
|
||||
more = iter.ReadObjectMore()
|
||||
}
|
||||
}
|
||||
func BlockingConfiguration_json_unmarshal_field(iter *jsoniter.Iterator, field string, out *BlockingConfiguration) bool {
|
||||
switch {
|
||||
case field == `timeout`:
|
||||
iter.ReadString(&(*out).Timeout)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func BlockingConfiguration_json_marshal(stream *jsoniter.Stream, val BlockingConfiguration) {
|
||||
stream.WriteObjectHead()
|
||||
BlockingConfiguration_json_marshal_field(stream, val)
|
||||
stream.WriteObjectTail()
|
||||
}
|
||||
func BlockingConfiguration_json_marshal_field(stream *jsoniter.Stream, val BlockingConfiguration) {
|
||||
stream.WriteObjectField(`timeout`)
|
||||
stream.WriteString(val.Timeout)
|
||||
stream.WriteMore()
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package main
|
||||
|
||||
import jsoniter "github.com/json-iterator/tinygo"
|
||||
|
||||
type Config_json struct {
|
||||
}
|
||||
func (json Config_json) Type() interface{} {
|
||||
var val Config
|
||||
return val
|
||||
}
|
||||
func (json Config_json) Unmarshal(iter *jsoniter.Iterator, out interface{}) {
|
||||
Config_json_unmarshal(iter, out.(*Config))
|
||||
}
|
||||
func (json Config_json) Marshal(stream *jsoniter.Stream, val interface{}) {
|
||||
Config_json_marshal(stream, val.(Config))
|
||||
}
|
||||
func Config_json_unmarshal(iter *jsoniter.Iterator, out *Config) {
|
||||
more := iter.ReadObjectHead()
|
||||
for more {
|
||||
field := iter.ReadObjectField()
|
||||
if !Config_json_unmarshal_field(iter, field, out) {
|
||||
iter.Skip()
|
||||
}
|
||||
more = iter.ReadObjectMore()
|
||||
}
|
||||
}
|
||||
func Config_json_unmarshal_field(iter *jsoniter.Iterator, field string, out *Config) bool {
|
||||
switch {
|
||||
case field == `sablier_url`:
|
||||
iter.ReadString(&(*out).SablierURL)
|
||||
return true
|
||||
case field == `cluster`:
|
||||
iter.ReadString(&(*out).Cluster)
|
||||
return true
|
||||
case field == `names`:
|
||||
Config_array1_json_unmarshal(iter, &(*out).Names)
|
||||
return true
|
||||
case field == `group`:
|
||||
iter.ReadString(&(*out).Group)
|
||||
return true
|
||||
case field == `session_duration`:
|
||||
iter.ReadString(&(*out).SessionDuration)
|
||||
return true
|
||||
case field == `dynamic`:
|
||||
Config_ptr2_json_unmarshal(iter, &(*out).Dynamic)
|
||||
return true
|
||||
case field == `blocking`:
|
||||
Config_ptr3_json_unmarshal(iter, &(*out).Blocking)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func Config_array1_json_unmarshal (iter *jsoniter.Iterator, out *[]string) {
|
||||
i := 0
|
||||
val := *out
|
||||
more := iter.ReadArrayHead()
|
||||
for more {
|
||||
if i == len(val) {
|
||||
val = append(val, make([]string, 4)...)
|
||||
}
|
||||
iter.ReadString(&val[i])
|
||||
i++
|
||||
more = iter.ReadArrayMore()
|
||||
}
|
||||
if i == 0 {
|
||||
*out = []string{}
|
||||
} else {
|
||||
*out = val[:i]
|
||||
}
|
||||
}
|
||||
func Config_ptr2_json_unmarshal (iter *jsoniter.Iterator, out **DynamicConfiguration) {
|
||||
var val DynamicConfiguration
|
||||
DynamicConfiguration_json_unmarshal(iter, &val)
|
||||
if iter.Error == nil {
|
||||
*out = &val
|
||||
}
|
||||
}
|
||||
func Config_ptr3_json_unmarshal (iter *jsoniter.Iterator, out **BlockingConfiguration) {
|
||||
var val BlockingConfiguration
|
||||
BlockingConfiguration_json_unmarshal(iter, &val)
|
||||
if iter.Error == nil {
|
||||
*out = &val
|
||||
}
|
||||
}
|
||||
func Config_json_marshal(stream *jsoniter.Stream, val Config) {
|
||||
stream.WriteObjectHead()
|
||||
Config_json_marshal_field(stream, val)
|
||||
stream.WriteObjectTail()
|
||||
}
|
||||
func Config_json_marshal_field(stream *jsoniter.Stream, val Config) {
|
||||
stream.WriteObjectField(`sablier_url`)
|
||||
stream.WriteString(val.SablierURL)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`cluster`)
|
||||
stream.WriteString(val.Cluster)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`names`)
|
||||
Config_array4_json_marshal(stream, val.Names)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`group`)
|
||||
stream.WriteString(val.Group)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`session_duration`)
|
||||
stream.WriteString(val.SessionDuration)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`dynamic`)
|
||||
if val.Dynamic == nil {
|
||||
stream.WriteNull()
|
||||
} else {
|
||||
DynamicConfiguration_json_marshal(stream, *val.Dynamic)
|
||||
}
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`blocking`)
|
||||
if val.Blocking == nil {
|
||||
stream.WriteNull()
|
||||
} else {
|
||||
BlockingConfiguration_json_marshal(stream, *val.Blocking)
|
||||
}
|
||||
stream.WriteMore()
|
||||
}
|
||||
func Config_array4_json_marshal (stream *jsoniter.Stream, val []string) {
|
||||
if len(val) == 0 {
|
||||
stream.WriteEmptyArray()
|
||||
} else {
|
||||
stream.WriteArrayHead()
|
||||
for i, elem := range val {
|
||||
if i != 0 { stream.WriteMore() }
|
||||
stream.WriteString(elem)
|
||||
}
|
||||
stream.WriteArrayTail()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
FROM golang:1.24 AS build
|
||||
|
||||
WORKDIR /go/src/sablier/plugins/proxywasm
|
||||
|
||||
COPY go.mod ./
|
||||
COPY go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . /go/src/sablier/plugins/proxywasm
|
||||
|
||||
RUN make build
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=build /go/src/sablier/plugins/proxywasm/sablierproxywasm.wasm ./plugin.wasm
|
||||
@@ -1,73 +0,0 @@
|
||||
package main
|
||||
|
||||
import jsoniter "github.com/json-iterator/tinygo"
|
||||
|
||||
type DynamicConfiguration_json struct {
|
||||
}
|
||||
func (json DynamicConfiguration_json) Type() interface{} {
|
||||
var val DynamicConfiguration
|
||||
return val
|
||||
}
|
||||
func (json DynamicConfiguration_json) Unmarshal(iter *jsoniter.Iterator, out interface{}) {
|
||||
DynamicConfiguration_json_unmarshal(iter, out.(*DynamicConfiguration))
|
||||
}
|
||||
func (json DynamicConfiguration_json) Marshal(stream *jsoniter.Stream, val interface{}) {
|
||||
DynamicConfiguration_json_marshal(stream, val.(DynamicConfiguration))
|
||||
}
|
||||
func DynamicConfiguration_json_unmarshal(iter *jsoniter.Iterator, out *DynamicConfiguration) {
|
||||
more := iter.ReadObjectHead()
|
||||
for more {
|
||||
field := iter.ReadObjectField()
|
||||
if !DynamicConfiguration_json_unmarshal_field(iter, field, out) {
|
||||
iter.Skip()
|
||||
}
|
||||
more = iter.ReadObjectMore()
|
||||
}
|
||||
}
|
||||
func DynamicConfiguration_json_unmarshal_field(iter *jsoniter.Iterator, field string, out *DynamicConfiguration) bool {
|
||||
switch {
|
||||
case field == `display_name`:
|
||||
iter.ReadString(&(*out).DisplayName)
|
||||
return true
|
||||
case field == `show_details`:
|
||||
DynamicConfiguration_ptr1_json_unmarshal(iter, &(*out).ShowDetails)
|
||||
return true
|
||||
case field == `theme`:
|
||||
iter.ReadString(&(*out).Theme)
|
||||
return true
|
||||
case field == `refresh_frequency`:
|
||||
iter.ReadString(&(*out).RefreshFrequency)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func DynamicConfiguration_ptr1_json_unmarshal (iter *jsoniter.Iterator, out **bool) {
|
||||
var val bool
|
||||
iter.ReadBool(&val)
|
||||
if iter.Error == nil {
|
||||
*out = &val
|
||||
}
|
||||
}
|
||||
func DynamicConfiguration_json_marshal(stream *jsoniter.Stream, val DynamicConfiguration) {
|
||||
stream.WriteObjectHead()
|
||||
DynamicConfiguration_json_marshal_field(stream, val)
|
||||
stream.WriteObjectTail()
|
||||
}
|
||||
func DynamicConfiguration_json_marshal_field(stream *jsoniter.Stream, val DynamicConfiguration) {
|
||||
stream.WriteObjectField(`display_name`)
|
||||
stream.WriteString(val.DisplayName)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`show_details`)
|
||||
if val.ShowDetails == nil {
|
||||
stream.WriteNull()
|
||||
} else {
|
||||
stream.WriteBool(*val.ShowDetails)
|
||||
}
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`theme`)
|
||||
stream.WriteString(val.Theme)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(`refresh_frequency`)
|
||||
stream.WriteString(val.RefreshFrequency)
|
||||
stream.WriteMore()
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
build:
|
||||
go generate
|
||||
env GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o sablierproxywasm.wasm .
|
||||
|
||||
docker:
|
||||
docker build -t sablierapp/sablier-proxy-wasm:latest .
|
||||
@@ -1,10 +0,0 @@
|
||||
> [!IMPORTANT]
|
||||
> This plugin now has its own reepository: https://github.com/sablierapp/sablier-proxywasm-plugin
|
||||
>
|
||||
> This plugin will no longer receive updates.
|
||||
|
||||
# Proxy Wasm Sablier Plugin
|
||||
|
||||
See more at
|
||||
- https://github.com/proxy-wasm/spec
|
||||
- https://github.com/proxy-wasm/proxy-wasm-go-sdk
|
||||
@@ -1,2 +0,0 @@
|
||||
# Sablier ProxyWasm Plugin integration with Apache APISIX
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
routes:
|
||||
- uri: "/dynamic/whoami"
|
||||
plugins:
|
||||
proxywasm_sablier_plugin:
|
||||
conf: '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1"], "session_duration": "1m", "dynamic": { "display_name": "Dynamic Whoami", "theme": "hacker-terminal" } }'
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"whoami:80": 1
|
||||
|
||||
- uri: "/blocking/whoami"
|
||||
plugins:
|
||||
proxywasm_sablier_plugin:
|
||||
conf: '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1"], "session_duration": "1m", "blocking": { "timeout": "30s" } }'
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"whoami:80": 1
|
||||
|
||||
- uri: "/multiple/whoami"
|
||||
plugins:
|
||||
proxywasm_sablier_plugin:
|
||||
conf: '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1", "docker_classic_e2e-nginx-1"], "session_duration": "1m", "dynamic": { "display_name": "Multiple Whoami" } }'
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"whoami:80": 1
|
||||
|
||||
- uri: "/multiple/nginx"
|
||||
plugins:
|
||||
proxywasm_sablier_plugin:
|
||||
conf: '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1", "docker_classic_e2e-nginx-1"], "session_duration": "1m", "dynamic": { "display_name": "Multiple Whoami" } }'
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"nginx:80": 1
|
||||
|
||||
- uri: "/healthy/nginx"
|
||||
plugins:
|
||||
proxywasm_sablier_plugin:
|
||||
conf: '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-nginx-1"], "session_duration": "1m", "dynamic": { "display_name": "Healthy Nginx" } }'
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"nginx:80": 1
|
||||
|
||||
- uri: "/group"
|
||||
plugins:
|
||||
proxywasm_sablier_plugin:
|
||||
conf: '{ "sablier_url": "sablier:10000", "group": "E2E", "session_duration": "1m", "dynamic": { "display_name": "Group E2E" } }'
|
||||
upstream:
|
||||
type: roundrobin
|
||||
nodes:
|
||||
"whoami:80": 1
|
||||
#END
|
||||
@@ -1,37 +0,0 @@
|
||||
services:
|
||||
apisix:
|
||||
image: apache/apisix:3.11.0-debian
|
||||
restart: always
|
||||
volumes:
|
||||
- ./config.yaml:/usr/local/apisix/conf/config.yaml:ro
|
||||
- ./apisix.yaml:/usr/local/apisix/conf/apisix.yaml:ro
|
||||
- ../../../sablierproxywasm.wasm:/wasm/sablierproxywasm.wasm
|
||||
ports:
|
||||
- "8080:9080/tcp"
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
@@ -1,11 +0,0 @@
|
||||
deployment:
|
||||
role: data_plane
|
||||
role_data_plane:
|
||||
config_provider: yaml
|
||||
|
||||
wasm:
|
||||
plugins:
|
||||
- name: proxywasm_sablier_plugin
|
||||
priority: 7997
|
||||
file: /wasm/sablierproxywasm.wasm
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOCKER_COMPOSE_FILE=compose.yaml
|
||||
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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
docker compose -f ${DOCKER_COMPOSE_FILE} -p ${DOCKER_COMPOSE_PROJECT_NAME} logs sablier apisix
|
||||
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
|
||||
run_docker_classic_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,36 +0,0 @@
|
||||
services:
|
||||
envoy:
|
||||
image: envoyproxy/envoy:v1.33-latest
|
||||
command: /usr/local/bin/envoy -c /etc/envoy.yaml
|
||||
volumes:
|
||||
- ./envoy.yaml:/etc/envoy.yaml
|
||||
- ../../../sablierproxywasm.wasm:/etc/sablierproxywasm.wasm
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
@@ -1,268 +0,0 @@
|
||||
static_resources:
|
||||
listeners:
|
||||
- name: main
|
||||
address:
|
||||
socket_address:
|
||||
address: 0.0.0.0
|
||||
port_value: 8080
|
||||
filter_chains:
|
||||
- filters:
|
||||
# Dynamic Whoami
|
||||
- name: envoy.filters.network.http_connection_manager
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||
codec_type: auto
|
||||
stat_prefix: ingress_http
|
||||
route_config:
|
||||
name: local_route
|
||||
virtual_hosts:
|
||||
- name: local_service
|
||||
domains: ["*"]
|
||||
routes:
|
||||
- match:
|
||||
prefix: "/dynamic/whoami"
|
||||
route:
|
||||
cluster: whoami
|
||||
typed_per_filter_config:
|
||||
sablier-wasm-whoami-dynamic:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
|
||||
config: # Note this config field could not be empty because the xDS API requirement.
|
||||
"@type": type.googleapis.com/google.protobuf.Empty # Empty as a placeholder.
|
||||
is_optional: true
|
||||
- match:
|
||||
path: "/blocking/whoami"
|
||||
route:
|
||||
cluster: whoami
|
||||
typed_per_filter_config:
|
||||
sablier-wasm-whoami-blocking:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
|
||||
config: # Note this config field could not be empty because the xDS API requirement.
|
||||
"@type": type.googleapis.com/google.protobuf.Empty # Empty as a placeholder.
|
||||
is_optional: true
|
||||
- match:
|
||||
prefix: "/multiple/whoami"
|
||||
route:
|
||||
cluster: whoami
|
||||
typed_per_filter_config:
|
||||
sablier-wasm-multiple:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
|
||||
config: # Note this config field could not be empty because the xDS API requirement.
|
||||
"@type": type.googleapis.com/google.protobuf.Empty # Empty as a placeholder.
|
||||
is_optional: true
|
||||
- match:
|
||||
path: "/multiple/nginx"
|
||||
route:
|
||||
cluster: nginx
|
||||
typed_per_filter_config:
|
||||
sablier-wasm-multiple:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
|
||||
config: # Note this config field could not be empty because the xDS API requirement.
|
||||
"@type": type.googleapis.com/google.protobuf.Empty # Empty as a placeholder.
|
||||
is_optional: true
|
||||
- match:
|
||||
path: "/healthy/nginx"
|
||||
route:
|
||||
cluster: nginx
|
||||
typed_per_filter_config:
|
||||
sablier-wasm-healthy:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
|
||||
config: # Note this config field could not be empty because the xDS API requirement.
|
||||
"@type": type.googleapis.com/google.protobuf.Empty # Empty as a placeholder.
|
||||
is_optional: true
|
||||
- match:
|
||||
path: "/group"
|
||||
route:
|
||||
cluster: whoami
|
||||
typed_per_filter_config:
|
||||
sablier-wasm-group:
|
||||
"@type": type.googleapis.com/envoy.config.route.v3.FilterConfig
|
||||
config: # Note this config field could not be empty because the xDS API requirement.
|
||||
"@type": type.googleapis.com/google.protobuf.Empty # Empty as a placeholder.
|
||||
is_optional: true
|
||||
|
||||
http_filters:
|
||||
- name: sablier-wasm-whoami-dynamic
|
||||
disabled: true
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
|
||||
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
|
||||
value:
|
||||
config:
|
||||
name: "sablier-wasm-whoami-dynamic"
|
||||
root_id: "sablier-wasm-whoami-dynamic"
|
||||
configuration:
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||
value: |
|
||||
{
|
||||
"sablier_url": "sablier:10000",
|
||||
"cluster": "sablier",
|
||||
"names": ["docker_classic_e2e-whoami-1"],
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Dynamic Whoami",
|
||||
"theme": "hacker-terminal"
|
||||
}
|
||||
}
|
||||
vm_config:
|
||||
runtime: "envoy.wasm.runtime.v8"
|
||||
vm_id: "vm.sablier.sablier-wasm-whoami-dynamic"
|
||||
code:
|
||||
local:
|
||||
filename: "/etc/sablierproxywasm.wasm"
|
||||
configuration: { }
|
||||
- name: sablier-wasm-whoami-blocking
|
||||
disabled: true
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
|
||||
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
|
||||
value:
|
||||
config:
|
||||
name: "sablier-wasm-whoami-blocking"
|
||||
root_id: "sablier-wasm-whoami-blocking"
|
||||
configuration:
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||
value: |
|
||||
{
|
||||
"sablier_url": "sablier:10000",
|
||||
"cluster": "sablier",
|
||||
"names": ["docker_classic_e2e-whoami-1"],
|
||||
"session_duration": "1m",
|
||||
"blocking": {
|
||||
"timeout": "30s"
|
||||
}
|
||||
}
|
||||
vm_config:
|
||||
runtime: "envoy.wasm.runtime.v8"
|
||||
vm_id: "vm.sablier.sablier-wasm-whoami-blocking"
|
||||
code:
|
||||
local:
|
||||
filename: "/etc/sablierproxywasm.wasm"
|
||||
configuration: { }
|
||||
- name: sablier-wasm-multiple
|
||||
disabled: true
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
|
||||
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
|
||||
value:
|
||||
config:
|
||||
name: "sablier-wasm-multiple"
|
||||
root_id: "sablier-wasm-multiple"
|
||||
configuration:
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||
value: |
|
||||
{
|
||||
"sablier_url": "sablier:10000",
|
||||
"cluster": "sablier",
|
||||
"names": ["docker_classic_e2e-whoami-1", "docker_classic_e2e-nginx-1"],
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Multiple Whoami"
|
||||
}
|
||||
}
|
||||
vm_config:
|
||||
runtime: "envoy.wasm.runtime.v8"
|
||||
vm_id: "vm.sablier.sablier-wasm-multiple"
|
||||
code:
|
||||
local:
|
||||
filename: "/etc/sablierproxywasm.wasm"
|
||||
configuration: { }
|
||||
- name: sablier-wasm-healthy
|
||||
disabled: true
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
|
||||
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
|
||||
value:
|
||||
config:
|
||||
name: "sablier-wasm-healthy"
|
||||
root_id: "sablier-wasm-healthy"
|
||||
configuration:
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||
value: |
|
||||
{
|
||||
"sablier_url": "sablier:10000",
|
||||
"cluster": "sablier",
|
||||
"names": ["docker_classic_e2e-nginx-1"],
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Healthy Nginx"
|
||||
}
|
||||
}
|
||||
vm_config:
|
||||
runtime: "envoy.wasm.runtime.v8"
|
||||
vm_id: "vm.sablier.sablier-wasm-healthy"
|
||||
code:
|
||||
local:
|
||||
filename: "/etc/sablierproxywasm.wasm"
|
||||
configuration: { }
|
||||
- name: sablier-wasm-group
|
||||
disabled: true
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
|
||||
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
|
||||
value:
|
||||
config:
|
||||
name: "sablier-wasm-group"
|
||||
root_id: "sablier-wasm-group"
|
||||
configuration:
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||
value: |
|
||||
{
|
||||
"sablier_url": "sablier:10000",
|
||||
"cluster": "sablier",
|
||||
"group": "E2E",
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Group E2E"
|
||||
}
|
||||
}
|
||||
vm_config:
|
||||
runtime: "envoy.wasm.runtime.v8"
|
||||
vm_id: "vm.sablier.sablier-wasm-group"
|
||||
code:
|
||||
local:
|
||||
filename: "/etc/sablierproxywasm.wasm"
|
||||
configuration: { }
|
||||
- name: envoy.filters.http.router
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
|
||||
|
||||
clusters:
|
||||
- name: sablier
|
||||
connect_timeout: 0.25s
|
||||
type: STRICT_DNS
|
||||
lb_policy: round_robin
|
||||
load_assignment:
|
||||
cluster_name: sablier
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: sablier
|
||||
port_value: 10000
|
||||
- name: whoami
|
||||
connect_timeout: 0.25s
|
||||
type: STRICT_DNS
|
||||
lb_policy: round_robin
|
||||
load_assignment:
|
||||
cluster_name: whoami
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: whoami
|
||||
port_value: 80
|
||||
- name: nginx
|
||||
connect_timeout: 0.25s
|
||||
type: STRICT_DNS
|
||||
lb_policy: round_robin
|
||||
load_assignment:
|
||||
cluster_name: nginx
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: nginx
|
||||
port_value: 80
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOCKER_COMPOSE_FILE=compose.yaml
|
||||
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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
docker compose -f ${DOCKER_COMPOSE_FILE} -p ${DOCKER_COMPOSE_PROJECT_NAME} logs sablier envoy
|
||||
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
|
||||
run_docker_classic_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,27 +0,0 @@
|
||||
# Install kubectl
|
||||
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
|
||||
|
||||
# Install helm3
|
||||
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
|
||||
# Start k3s
|
||||
docker compose up -d
|
||||
sudo chown vscode ./kubeconfig.yaml
|
||||
chmod 600 ./kubeconfig.yaml
|
||||
export KUBECONFIG=./kubeconfig.yaml
|
||||
|
||||
kubectl create configmap -n istio-system sablier-wasm-plugin --from-file ../../sablierproxywasm.wasm
|
||||
|
||||
# Install Istio Helm charts
|
||||
helm repo add istio https://istio-release.storage.googleapis.com/charts
|
||||
helm repo update
|
||||
helm install istio-base istio/base -n istio-system --wait
|
||||
helm install istiod istio/istiod -n istio-system --wait
|
||||
kubectl label namespace istio-system istio-injection=enabled
|
||||
helm install istio-ingressgateway istio/gateway --values ./istio-gateway-values.yaml -n istio-system --wait
|
||||
|
||||
# Install Sablier
|
||||
kubectl apply -f ./manifests/sablier.yml
|
||||
|
||||
# Build proxywasm
|
||||
make docker
|
||||
@@ -1,24 +0,0 @@
|
||||
version: '3'
|
||||
services:
|
||||
server:
|
||||
image: "rancher/k3s:v1.31.5-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
|
||||
ports:
|
||||
- 6443:6443 # Kubernetes API Server
|
||||
- 8080:80 # Ingress controller port 80
|
||||
@@ -1,8 +0,0 @@
|
||||
volumes:
|
||||
- name: wasmfilter
|
||||
configMap:
|
||||
name: sablier-wasm-plugin
|
||||
|
||||
volumeMounts:
|
||||
- name: wasmfilter
|
||||
mountPath: /opt/filters/sablierproxywasm.wasm
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: gateway
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
istio: ingressgateway
|
||||
servers:
|
||||
- port:
|
||||
number: 80
|
||||
name: http
|
||||
protocol: HTTP
|
||||
hosts:
|
||||
- "*"
|
||||
@@ -1,55 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
version: v1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
version: v1
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.29.2
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app: nginx
|
||||
service: nginx
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
hosts:
|
||||
- "*"
|
||||
gateways:
|
||||
- gateway.istio-system.svc.cluster.local
|
||||
http:
|
||||
- match:
|
||||
- uri:
|
||||
prefix: "/multiple/nginx"
|
||||
- uri:
|
||||
prefix: "/healthy/nginx"
|
||||
route:
|
||||
- destination:
|
||||
port:
|
||||
number: 80
|
||||
host: nginx
|
||||
@@ -1,83 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: sablier-system
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: sablier-system
|
||||
labels:
|
||||
app: sablier
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sablier
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sablier
|
||||
spec:
|
||||
serviceAccountName: sablier
|
||||
containers:
|
||||
- name: sablier
|
||||
image: sablierapp/sablier:local
|
||||
args: ["start", "--provider.name=kubernetes", "--logging.level=debug"]
|
||||
ports:
|
||||
- containerPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: sablier-system
|
||||
spec:
|
||||
selector:
|
||||
app: sablier
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 10000
|
||||
targetPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: sablier-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: sablier-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
- ""
|
||||
resources:
|
||||
- deployments
|
||||
- deployments/scale
|
||||
- statefulsets
|
||||
- statefulsets/scale
|
||||
verbs:
|
||||
- patch # Scale up and down
|
||||
- get # Retrieve info about specific dep
|
||||
- update # Scale up and down
|
||||
- list # Events
|
||||
- watch # Events
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: sablier-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: sablier
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: sablier
|
||||
namespace: sablier-system
|
||||
@@ -1,96 +0,0 @@
|
||||
apiVersion: extensions.istio.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: sablier-wasm-whoami-dynamic
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: ingressgateway
|
||||
url: file:///opt/filters/sablierproxywasm.wasm/..data/sablierproxywasm.wasm
|
||||
# Use https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/#WasmPlugin-TrafficSelector
|
||||
# To specify which service to apply this filter only
|
||||
phase: UNSPECIFIED_PHASE
|
||||
pluginConfig:
|
||||
{
|
||||
"sablier_url": "sablier.sablier-system.svc.cluster.local",
|
||||
"cluster": "outbound|10000||sablier.sablier-system.svc.cluster.local",
|
||||
"names": [ "deployment_default_whoami_1" ],
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Dynamic Whoami",
|
||||
"theme": "hacker-terminal"
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: extensions.istio.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: sablier-wasm-whoami-blocking
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: ingressgateway
|
||||
url: file:///opt/filters/sablierproxywasm.wasm/..data/sablierproxywasm.wasm
|
||||
# Use https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/#WasmPlugin-TrafficSelector
|
||||
# To specify which service to apply this filter only
|
||||
phase: UNSPECIFIED_PHASE
|
||||
pluginConfig:
|
||||
{
|
||||
"sablier_url": "sablier.sablier-system.svc.cluster.local",
|
||||
"cluster": "outbound|10000||sablier.sablier-system.svc.cluster.local",
|
||||
"names": [ "deployment_default_whoami_1" ],
|
||||
"session_duration": "1m",
|
||||
"blocking": {
|
||||
"timeout": "30s"
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: extensions.istio.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: sablier-wasm-multiple
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: ingressgateway
|
||||
url: file:///opt/filters/sablierproxywasm.wasm/..data/sablierproxywasm.wasm
|
||||
# Use https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/#WasmPlugin-TrafficSelector
|
||||
# To specify which service to apply this filter only
|
||||
phase: UNSPECIFIED_PHASE
|
||||
pluginConfig:
|
||||
{
|
||||
"sablier_url": "sablier.sablier-system.svc.cluster.local",
|
||||
"cluster": "outbound|10000||sablier.sablier-system.svc.cluster.local",
|
||||
"names": [ "deployment_default_whoami_1", "deployment_default_nginx_1" ],
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Multiple Whoami"
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: extensions.istio.io/v1alpha1
|
||||
kind: WasmPlugin
|
||||
metadata:
|
||||
name: sablier-wasm-healthy
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: ingressgateway
|
||||
url: file:///opt/filters/sablierproxywasm.wasm/..data/sablierproxywasm.wasm
|
||||
# Use https://istio.io/latest/docs/reference/config/proxy_extensions/wasm-plugin/#WasmPlugin-TrafficSelector
|
||||
# To specify which service to apply this filter only
|
||||
phase: UNSPECIFIED_PHASE
|
||||
pluginConfig:
|
||||
{
|
||||
"sablier_url": "sablier.sablier-system.svc.cluster.local",
|
||||
"cluster": "outbound|10000||sablier.sablier-system.svc.cluster.local",
|
||||
"names": [ "deployment_default_nginx_1" ],
|
||||
"session_duration": "1m",
|
||||
"dynamic": {
|
||||
"display_name": "Healthy Nginx"
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
version: v1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
version: v1
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami
|
||||
labels:
|
||||
app: whoami
|
||||
service: whoami
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: whoami
|
||||
spec:
|
||||
hosts:
|
||||
- "*"
|
||||
gateways:
|
||||
- gateway.istio-system.svc.cluster.local
|
||||
http:
|
||||
- match:
|
||||
- uri:
|
||||
prefix: "/dynamic/whoami"
|
||||
- uri:
|
||||
prefix: "/blocking/whoami"
|
||||
- uri:
|
||||
prefix: "/multiple/whoami"
|
||||
route:
|
||||
- destination:
|
||||
port:
|
||||
number: 80
|
||||
host: whoami
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
export DOCKER_COMPOSE_FILE=compose.yaml
|
||||
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 sablierapp/sablier:local into k3s..."
|
||||
docker save sablierapp/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_istio() {
|
||||
helm repo add istio https://istio-release.storage.googleapis.com/charts
|
||||
helm repo update
|
||||
kubectl create namespace istio-system
|
||||
helm install istio-base istio/base -n istio-system --wait
|
||||
helm install istiod istio/istiod -n istio-system --wait
|
||||
kubectl label namespace istio-system istio-injection=enabled
|
||||
kubectl label namespace default istio-injection=enabled
|
||||
kubectl create configmap -n istio-system sablier-wasm-plugin --from-file ../../../sablierproxywasm.wasm
|
||||
helm install istio-ingressgateway istio/gateway --values ./istio-gateway-values.yaml -n istio-system --wait
|
||||
}
|
||||
|
||||
prepare_manifests() {
|
||||
kubectl apply -f ./manifests
|
||||
}
|
||||
|
||||
destroy_manifests() {
|
||||
kubectl delete -f ./manifests
|
||||
}
|
||||
|
||||
run_kubernetes_test() {
|
||||
echo "---- Running Kubernetes Test: $1 ----"
|
||||
prepare_manifests
|
||||
sleep 10
|
||||
go clean -testcache
|
||||
if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
kubectl -n kube-system logs deployments/sablier-deployment
|
||||
# kubectl -n kube-system logs deployments/traefik TODO: Log istio
|
||||
fi
|
||||
|
||||
destroy_manifests
|
||||
}
|
||||
|
||||
# trap destroy_kubernetes EXIT
|
||||
|
||||
prepare_kubernetes
|
||||
prepare_istio
|
||||
# run_kubernetes_test Test_Dynamic
|
||||
# run_kubernetes_test Test_Blocking
|
||||
# run_kubernetes_test Test_Multiple
|
||||
# run_kubernetes_test Test_Healthy
|
||||
|
||||
# exit $errors
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM ubuntu:25.10
|
||||
|
||||
RUN apt update && apt install -y libatomic1
|
||||
|
||||
ADD https://github.com/Kong/ngx_wasm_module/releases/download/prerelease-0.6.0/wasmx-prerelease-0.6.0-v8-x86_64-ubuntu22.04.tar.gz wasmx.tar.gz
|
||||
|
||||
RUN mkdir /etc/nginx
|
||||
RUN tar -xvf wasmx.tar.gz
|
||||
RUN mv /wasmx-prerelease-0.6.0-v8-x86_64-ubuntu22.04/* /etc/nginx/
|
||||
|
||||
WORKDIR /etc/nginx
|
||||
|
||||
CMD [ "./nginx", "-g", "daemon off;" ]
|
||||
@@ -1,37 +0,0 @@
|
||||
services:
|
||||
reverseproxy:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
- ../../../sablierproxywasm.wasm:/wasm/sablierproxywasm.wasm
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
@@ -1,70 +0,0 @@
|
||||
# nginx.conf
|
||||
events {}
|
||||
|
||||
# nginx master process gets a default 'main' VM
|
||||
# a new top-level configuration block receives all configuration for this main VM
|
||||
wasm {
|
||||
module proxywasm_sablier_plugin /wasm/sablierproxywasm.wasm;
|
||||
}
|
||||
|
||||
error_log /dev/stdout info;
|
||||
|
||||
# each nginx worker process is able to instantiate wasm modules in its subsystems
|
||||
http {
|
||||
access_log /dev/stdout;
|
||||
|
||||
# internal docker resolver, see /etc/resolv.conf on proxy container
|
||||
resolver 127.0.0.11 valid=1s ipv6=off;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
|
||||
location /dynamic/whoami {
|
||||
proxy_wasm proxywasm_sablier_plugin '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1"], "session_duration": "1m", "dynamic": { "display_name": "Dynamic Whoami", "theme": "hacker-terminal" } }';
|
||||
|
||||
set $proxy_pass_host whoami:80$request_uri;
|
||||
proxy_pass http://$proxy_pass_host;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
}
|
||||
|
||||
location /blocking/whoami {
|
||||
wasm_socket_read_timeout 60s; # Blocking hangs the request
|
||||
proxy_wasm proxywasm_sablier_plugin '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1"], "session_duration": "1m", "blocking": { "timeout": "30s" } }';
|
||||
|
||||
set $proxy_pass_host whoami:80$request_uri;
|
||||
proxy_pass http://$proxy_pass_host;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
}
|
||||
|
||||
location /multiple/whoami {
|
||||
proxy_wasm proxywasm_sablier_plugin '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1", "docker_classic_e2e-nginx-1"], "session_duration": "1m", "dynamic": { "display_name": "Multiple Whoami" } }';
|
||||
|
||||
proxy_pass http://whoami:80$request_uri;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
}
|
||||
|
||||
location /multiple/nginx {
|
||||
proxy_wasm proxywasm_sablier_plugin '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-whoami-1", "docker_classic_e2e-nginx-1"], "session_duration": "1m", "dynamic": { "display_name": "Multiple Whoami" } }';
|
||||
|
||||
set $proxy_pass_host nginx:80$request_uri;
|
||||
proxy_pass http://$proxy_pass_host;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
}
|
||||
|
||||
location /healthy/nginx {
|
||||
proxy_wasm proxywasm_sablier_plugin '{ "sablier_url": "sablier:10000", "names": ["docker_classic_e2e-nginx-1"], "session_duration": "1m", "dynamic": { "display_name": "Healthy Nginx" } }';
|
||||
|
||||
set $proxy_pass_host nginx:80$request_uri;
|
||||
proxy_pass http://$proxy_pass_host;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
}
|
||||
|
||||
location /group {
|
||||
proxy_wasm proxywasm_sablier_plugin '{ "sablier_url": "sablier:10000", "group": "E2E", "session_duration": "1m", "dynamic": { "display_name": "Group E2E" } }';
|
||||
|
||||
set $proxy_pass_host whoami:80$request_uri;
|
||||
proxy_pass http://$proxy_pass_host;
|
||||
proxy_set_header Host localhost:8080; # e2e test compliance
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOCKER_COMPOSE_FILE=compose.yaml
|
||||
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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
docker compose -f ${DOCKER_COMPOSE_FILE} -p ${DOCKER_COMPOSE_PROJECT_NAME} logs sablier reverseproxy
|
||||
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
|
||||
run_docker_classic_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,21 +0,0 @@
|
||||
module github.com/sablierapp/sablier/plugins/proxy-wasm
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/json-iterator/tinygo v0.0.0-20211221071957-84b5b690c8a0
|
||||
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.24.0
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/tetratelabs/wazero v1.7.2 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -1,33 +0,0 @@
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/json-iterator/tinygo v0.0.0-20211221071957-84b5b690c8a0 h1:/cd98gHSKnKHcYYyBJ9qNvHxYZdXzJC8JMs42gMPp5I=
|
||||
github.com/json-iterator/tinygo v0.0.0-20211221071957-84b5b690c8a0/go.mod h1:sR5SXbtbtp8PxPu3yGjZug4AS5aAur8jQZl9DXYTpP0=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924 h1:wTcK6gcyTKJMeDka69AMjZYvisdI8CBXzTEfZ+2pOxI=
|
||||
github.com/proxy-wasm/proxy-wasm-go-sdk v0.0.0-20250212164326-ab4161dcf924/go.mod h1:9mBRvh8I6Td6sg3CwEY+zGFE4DKaIoieCaca1kQnDBE=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.24.0 h1:Xuwzknb4+OHBSYFXif0aBdV0F4MShL8L5YYFda9uUIs=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.24.0/go.mod h1:niJQcnEDtftzrVC0/qqlSs2Kzr1dwb7VxpIPHBO2XXk=
|
||||
github.com/tetratelabs/wazero v1.7.2 h1:1+z5nXJNwMLPAWaTePFi49SSTL0IMx/i3Fg8Yc25GDc=
|
||||
github.com/tetratelabs/wazero v1.7.2/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -1,321 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jsoniter "github.com/json-iterator/tinygo"
|
||||
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var Version string
|
||||
|
||||
func main() {}
|
||||
func init() {
|
||||
proxywasm.SetVMContext(&vmContext{})
|
||||
}
|
||||
|
||||
// vmContext implements types.VMContext interface of proxy-wasm-go SDK.
|
||||
type vmContext struct {
|
||||
// Embed the default VM context here,
|
||||
// so that we don't need to reimplement all the methods.
|
||||
types.DefaultVMContext
|
||||
}
|
||||
|
||||
// Override types.DefaultVMContext.
|
||||
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
|
||||
return &pluginContext{}
|
||||
}
|
||||
|
||||
type pluginContext struct {
|
||||
// Embed the default plugin context here,
|
||||
// so that we don't need to reimplement all the methods.
|
||||
types.DefaultPluginContext
|
||||
configuration pluginConfiguration
|
||||
}
|
||||
|
||||
type pluginConfiguration struct {
|
||||
cluster string
|
||||
method string
|
||||
path string
|
||||
authority string
|
||||
timeout uint32
|
||||
}
|
||||
|
||||
// newPluginConfiguration creates a pluginConfiguration with default values
|
||||
func newPluginConfiguration() pluginConfiguration {
|
||||
return pluginConfiguration{
|
||||
cluster: "sablier:10000",
|
||||
method: "GET",
|
||||
path: "/",
|
||||
authority: "sablier.cluster.local",
|
||||
timeout: 5000, // timeout in milliseconds
|
||||
}
|
||||
}
|
||||
|
||||
// Override types.DefaultPluginContext.
|
||||
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
|
||||
proxywasm.LogInfof("sablier proxywasm plugin version %v loaded", Version)
|
||||
data, err := proxywasm.GetPluginConfiguration()
|
||||
if err != nil && !errors.Is(err, types.ErrorStatusNotFound) {
|
||||
proxywasm.LogCriticalf("error reading plugin configuration: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
|
||||
proxywasm.LogInfof("plugin config: %s", string(data))
|
||||
config, err := parsePluginConfiguration(data)
|
||||
if err != nil {
|
||||
proxywasm.LogCriticalf("error parsing plugin configuration: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
|
||||
ctx.configuration = config
|
||||
|
||||
return types.OnPluginStartStatusOK
|
||||
}
|
||||
|
||||
//go:generate go run github.com/json-iterator/tinygo/gen
|
||||
type DynamicConfiguration struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
ShowDetails *bool `json:"show_details"`
|
||||
Theme string `json:"theme"`
|
||||
RefreshFrequency string `json:"refresh_frequency"`
|
||||
}
|
||||
|
||||
//go:generate go run github.com/json-iterator/tinygo/gen
|
||||
type BlockingConfiguration struct {
|
||||
Timeout string `json:"timeout"`
|
||||
}
|
||||
|
||||
//go:generate go run github.com/json-iterator/tinygo/gen
|
||||
type Config struct {
|
||||
// SablierURL in the format of hostname:port. The scheme is excluded
|
||||
SablierURL string `json:"sablier_url"`
|
||||
// Cluster is an optional value that allows you to set override the
|
||||
// first argument to `proxywasm.DispatchHttpCall`.
|
||||
// In istio for exemple, the expected value would be: "outbound|port||hostname", e.g.: "outbound|10000||sablier"
|
||||
// In APISIX and Nginx for example, the value would be the same as SablierURL, e.g.: sablier:10000
|
||||
// Defaults to the same value of `SablierURL`.
|
||||
Cluster string `json:"cluster"`
|
||||
Names []string `json:"names"`
|
||||
Group string `json:"group"`
|
||||
SessionDuration string `json:"session_duration"`
|
||||
Dynamic *DynamicConfiguration `json:"dynamic"`
|
||||
Blocking *BlockingConfiguration `json:"blocking"`
|
||||
}
|
||||
|
||||
func (c Config) GetPath() string {
|
||||
path := url.URL{}
|
||||
q := path.Query()
|
||||
|
||||
if c.SessionDuration != "" {
|
||||
dur, err := time.ParseDuration(c.SessionDuration)
|
||||
if err != nil {
|
||||
proxywasm.LogWarnf("parsing session duration failed (ignoring value): %v", err)
|
||||
} else {
|
||||
q.Add("session_duration", dur.String())
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range c.Names {
|
||||
q.Add("names", name)
|
||||
}
|
||||
|
||||
if c.Group != "" {
|
||||
q.Add("group", c.Group)
|
||||
}
|
||||
path.RawQuery = q.Encode()
|
||||
|
||||
if c.Dynamic != nil {
|
||||
return c.getDynamicQuery(path)
|
||||
} else if c.Blocking != nil {
|
||||
return c.getBlockingQuery(path)
|
||||
}
|
||||
return "no strategy configured"
|
||||
}
|
||||
|
||||
func (c Config) getDynamicQuery(path url.URL) string {
|
||||
path.Path = "/api/strategies/dynamic"
|
||||
q := path.Query()
|
||||
|
||||
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 != "" {
|
||||
dur, err := time.ParseDuration(c.Dynamic.RefreshFrequency)
|
||||
if err != nil {
|
||||
proxywasm.LogWarnf("parsing dynamic refresh frequency failed (ignoring value): %v", err)
|
||||
} else {
|
||||
q.Add("refresh_frequency", dur.String())
|
||||
}
|
||||
}
|
||||
|
||||
if c.Dynamic.ShowDetails != nil {
|
||||
q.Add("show_details", strconv.FormatBool(*c.Dynamic.ShowDetails))
|
||||
}
|
||||
path.RawQuery = q.Encode()
|
||||
|
||||
return path.String()
|
||||
}
|
||||
|
||||
func (c Config) getBlockingQuery(path url.URL) string {
|
||||
path.Path = "/api/strategies/blocking"
|
||||
q := path.Query()
|
||||
|
||||
if c.Blocking.Timeout != "" {
|
||||
dur, err := time.ParseDuration(c.Blocking.Timeout)
|
||||
if err != nil {
|
||||
proxywasm.LogWarnf("parsing blocking timeout duration failed (ignoring value): %v", err)
|
||||
} else {
|
||||
q.Add("timeout", dur.String())
|
||||
}
|
||||
}
|
||||
path.RawQuery = q.Encode()
|
||||
|
||||
return path.String()
|
||||
}
|
||||
|
||||
func parsePluginConfiguration(data []byte) (pluginConfiguration, error) {
|
||||
pluginConf := newPluginConfiguration()
|
||||
if len(data) == 0 {
|
||||
return pluginConf, fmt.Errorf("the plugin configuration is not a valid: %q", string(data))
|
||||
}
|
||||
|
||||
json := jsoniter.CreateJsonAdapter(Config_json{}, BlockingConfiguration_json{}, DynamicConfiguration_json{})
|
||||
|
||||
var c Config
|
||||
err := json.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
proxywasm.LogErrorf("error parsing configuration: %v", err.Error())
|
||||
return pluginConf, err
|
||||
}
|
||||
|
||||
if c.Blocking == nil && c.Dynamic == nil {
|
||||
return pluginConf, fmt.Errorf("you must specify one strategy (dynamic or blocking)")
|
||||
}
|
||||
|
||||
if c.Blocking != nil && c.Dynamic != nil {
|
||||
return pluginConf, fmt.Errorf("you must specify only one strategy")
|
||||
}
|
||||
|
||||
if c.Blocking != nil && c.Blocking.Timeout != "" {
|
||||
timeout, err := time.ParseDuration(c.Blocking.Timeout)
|
||||
if err != nil {
|
||||
return pluginConf, fmt.Errorf("cannot parse blocking timeout duration: %v", err)
|
||||
}
|
||||
pluginConf.timeout = uint32(timeout.Milliseconds())
|
||||
}
|
||||
|
||||
if len(c.Names) == 0 && len(c.Group) == 0 {
|
||||
return pluginConf, fmt.Errorf("you must specify names or group")
|
||||
}
|
||||
|
||||
if len(c.Names) > 0 && len(c.Group) > 0 {
|
||||
return pluginConf, fmt.Errorf("you must specify either names or group")
|
||||
}
|
||||
|
||||
if c.SablierURL != "" {
|
||||
pluginConf.authority = c.SablierURL
|
||||
|
||||
// Default to SablierURL
|
||||
pluginConf.cluster = c.SablierURL
|
||||
}
|
||||
|
||||
if c.Cluster != "" {
|
||||
pluginConf.cluster = c.Cluster
|
||||
}
|
||||
|
||||
pluginConf.path = c.GetPath()
|
||||
|
||||
return pluginConf, nil
|
||||
}
|
||||
|
||||
// Override types.DefaultPluginContext.
|
||||
func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
|
||||
|
||||
headers := [][2]string{
|
||||
{":method", ctx.configuration.method},
|
||||
{":path", ctx.configuration.path},
|
||||
{":authority", ctx.configuration.authority},
|
||||
{"User-Agent", fmt.Sprintf("sablier-proxywasm-plugin/%s", Version)},
|
||||
}
|
||||
return &httpOnDemand{
|
||||
contextID: contextID,
|
||||
headers: headers,
|
||||
cluster: ctx.configuration.cluster,
|
||||
timeout: ctx.configuration.timeout,
|
||||
}
|
||||
}
|
||||
|
||||
type httpOnDemand struct {
|
||||
// Embed the default http context here,
|
||||
// so that we don't need to reimplement all the methods.
|
||||
types.DefaultHttpContext
|
||||
contextID uint32
|
||||
headers [][2]string
|
||||
cluster string
|
||||
timeout uint32
|
||||
}
|
||||
|
||||
// Override types.DefaultHttpContext.
|
||||
func (ctx *httpOnDemand) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
|
||||
proxywasm.LogInfof("DispatchHttpCall to %v", ctx.cluster)
|
||||
proxywasm.LogInfof("DispatchHttpCall with headers %v", ctx.headers)
|
||||
if _, err := proxywasm.DispatchHttpCall(ctx.cluster, ctx.headers, nil, nil,
|
||||
ctx.timeout, httpCallResponseCallback); err != nil {
|
||||
proxywasm.LogCriticalf("dipatch httpcall failed: %v", err)
|
||||
proxywasm.LogDebugf("%s: %v", ctx.cluster, ctx.headers)
|
||||
return types.ActionContinue
|
||||
}
|
||||
|
||||
proxywasm.LogInfof("http call dispatched to %s", ctx.cluster)
|
||||
|
||||
return types.ActionPause
|
||||
}
|
||||
|
||||
func httpCallResponseCallback(numHeaders, bodySize, numTrailers int) {
|
||||
hs, err := proxywasm.GetHttpCallResponseHeaders()
|
||||
if err != nil {
|
||||
proxywasm.LogCriticalf("failed to get response headers: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
proxywasm.LogInfof("GetHttpCallResponseHeaders: %v", hs)
|
||||
|
||||
headerIndex := slices.IndexFunc(hs, func(h [2]string) bool { return strings.ToLower(h[0]) == "x-sablier-session-status" })
|
||||
if headerIndex < 0 {
|
||||
proxywasm.LogCriticalf("failed to find x-sablier-session-status header: %v", hs)
|
||||
proxywasm.ResumeHttpRequest()
|
||||
return
|
||||
}
|
||||
headerValue := hs[headerIndex][1]
|
||||
|
||||
if headerValue != "ready" {
|
||||
b, err := proxywasm.GetHttpCallResponseBody(0, bodySize)
|
||||
if err != nil {
|
||||
proxywasm.LogCriticalf("failed to get response body: %v", err)
|
||||
proxywasm.ResumeHttpRequest()
|
||||
return
|
||||
}
|
||||
|
||||
proxywasm.LogInfof("GetHttpCallResponseBody (%v bytes): %v", bodySize, string(b))
|
||||
|
||||
if err := proxywasm.SendHttpResponse(200, hs, b, -1); err != nil {
|
||||
proxywasm.LogErrorf("failed to send local response: %v", err)
|
||||
proxywasm.ResumeHttpRequest()
|
||||
}
|
||||
} else {
|
||||
proxywasm.ResumeHttpRequest()
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/proxytest"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
)
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
data := `{
|
||||
"sablier_url": "sablier",
|
||||
"sablier_port": 10000,
|
||||
"group": "demo",
|
||||
"session_duration": "30s",
|
||||
"dynamic": {
|
||||
"display_dame": "From WASM!",
|
||||
"show_details": true,
|
||||
"theme": "hacker-terminal",
|
||||
"refresh_frequency": "5s"
|
||||
}
|
||||
}`
|
||||
|
||||
config, err := parsePluginConfiguration([]byte(data))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Log("path:", config.path)
|
||||
t.Log("authority:", config.authority)
|
||||
}
|
||||
|
||||
func TestPluginContext_OnTick(t *testing.T) {
|
||||
vmTest(t, func(t *testing.T, vm types.VMContext) {
|
||||
data := `{
|
||||
"sablier_url": "sablier",
|
||||
"sablier_port": 10000,
|
||||
"group": "demo",
|
||||
"session_duration": "30s",
|
||||
"dynamic": {
|
||||
"display_dame": "From WASM!",
|
||||
"show_details": true,
|
||||
"theme": "hacker-terminal",
|
||||
"refresh_frequency": "5s"
|
||||
}
|
||||
}`
|
||||
opt := proxytest.NewEmulatorOption().WithVMContext(vm).WithPluginConfiguration([]byte(data))
|
||||
host, reset := proxytest.NewHostEmulator(opt)
|
||||
defer reset()
|
||||
|
||||
// Create http context.
|
||||
id := host.InitializeHttpContext()
|
||||
|
||||
// Call OnRequestHeaders.
|
||||
action := host.CallOnRequestHeaders(id, [][2]string{
|
||||
{"content-length", "10"},
|
||||
}, false)
|
||||
|
||||
// Must be continued.
|
||||
require.Equal(t, types.ActionPause, action)
|
||||
|
||||
// Check the final request headers
|
||||
host.CallOnHttpCallResponse(id, [][2]string{
|
||||
{"x-sablier-session-status", "not-ready"},
|
||||
}, nil, []byte("Response from Sablier"))
|
||||
response := host.GetCurrentResponseBody(id)
|
||||
require.Equal(t,
|
||||
"Response from Sablier",
|
||||
response,
|
||||
"response should be served from sablier.")
|
||||
})
|
||||
}
|
||||
|
||||
// vmTest executes f twice, once with a types.VMContext that executes plugin code directly
|
||||
// in the host, and again by executing the plugin code within the compiled main.wasm binary.
|
||||
// Execution with main.wasm will be skipped if the file cannot be found.
|
||||
func vmTest(t *testing.T, f func(*testing.T, types.VMContext)) {
|
||||
t.Helper()
|
||||
|
||||
t.Run("go", func(t *testing.T) {
|
||||
f(t, &vmContext{})
|
||||
})
|
||||
|
||||
t.Run("wasm", func(t *testing.T) {
|
||||
wasm, err := os.ReadFile("sablierproxywasm.wasm")
|
||||
if err != nil {
|
||||
t.Skip("wasm not found")
|
||||
}
|
||||
v, err := proxytest.NewWasmVMContext(wasm)
|
||||
require.NoError(t, err)
|
||||
defer v.Close()
|
||||
f(t, v)
|
||||
})
|
||||
}
|
||||
16
plugins/traefik/.gitignore
vendored
16
plugins/traefik/.gitignore
vendored
@@ -1,16 +0,0 @@
|
||||
# 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
|
||||
@@ -1,186 +0,0 @@
|
||||
> [!IMPORTANT]
|
||||
> This plugin now has its own reepository: https://github.com/sablierapp/sablier-traefik-plugin
|
||||
>
|
||||
> This plugin will no longer receive updates.
|
||||
|
||||
|
||||
# Traefik Sablier Plugin
|
||||
|
||||
- [Traefik Sablier Plugin](#traefik-sablier-plugin)
|
||||
- [Installation](#installation)
|
||||
- [Traefik with Docker classic](#traefik-with-docker-classic)
|
||||
- [Traefik with Docker Swarm](#traefik-with-docker-swarm)
|
||||
- [Traefik with Kubernetes](#traefik-with-kubernetes)
|
||||
- [Plugin](#plugin)
|
||||
- [Development](#development)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Add this snippet in the Traefik Static configuration
|
||||
|
||||
```yaml
|
||||
experimental:
|
||||
plugins:
|
||||
sablier:
|
||||
moduleName: "github.com/sablierapp/sablier"
|
||||
version: "v1.8.0-beta.22"
|
||||
```
|
||||
|
||||
2. Configure the plugin using the Dynamic Configuration. Example:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
my-sablier:
|
||||
plugin:
|
||||
sablier:
|
||||
sablierUrl: http://sablier:10000 # The sablier URL service, must be reachable from the Traefik instance
|
||||
names: whoami,nginx # Comma separated names of containers/services/deployments etc.
|
||||
sessionDuration: 1m # The session duration after which containers/services/deployments instances are shutdown
|
||||
# You can only use one strategy at a time
|
||||
# To do so, only declare `dynamic` or `blocking`
|
||||
|
||||
# Dynamic strategy, provides the waiting webui
|
||||
dynamic:
|
||||
displayName: My Title # (Optional) Defaults to the middleware name
|
||||
showDetails: true # (Optional) Set to true or false to show details specifcally for this middleware, unset to use Sablier server defaults
|
||||
theme: hacker-terminal # (Optional) The theme to use
|
||||
refreshFrequency: 5s # (Optional) The loading page refresh frequency
|
||||
|
||||
# Blocking strategy, waits until services are up and running
|
||||
# but will not wait more than `timeout`
|
||||
# blocking:
|
||||
# timeout: 1m
|
||||
```
|
||||
|
||||
You can also checkout the End to End tests here: [e2e](./e2e/).
|
||||
|
||||
## Traefik with Docker classic
|
||||
|
||||
|
||||
**Container labels cannot be used**
|
||||
|
||||
Traefik will evict the container from its pool if it's not running. Which means the middleware won't trigger on incoming request.
|
||||
|
||||
You must use the dynamic configuration to route to the container whatever the container state is.
|
||||
|
||||
**Example**
|
||||
|
||||
*docker-compose.yml*
|
||||
```yaml
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:2.9.1
|
||||
command:
|
||||
- --entryPoints.http.address=:80
|
||||
- --providers.docker=true
|
||||
- --providers.file.filename=/etc/traefik/dynamic-config.yml
|
||||
- --experimental.plugins.sablier.moduleName=github.com/sablierapp/sablier/plugins/traefik
|
||||
- --experimental.plugins.sablier.version=v1.10.1
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
- './dynamic-config.yml:/etc/traefik/dynamic-config.yml'
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:1.8.0
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
# Dynamic Middleware
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.names=sablier-whoami-1
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.sablierUrl=http://sablier:10000
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.dynamic.sessionDuration=1m
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
# Cannot use labels because as soon as the container is stopped, the labels are not treated by Traefik
|
||||
# The route doesn't exist anymore. Use dynamic-config.yml file instead.
|
||||
# labels:
|
||||
# - traefik.enable
|
||||
# - traefik.http.routers.whoami-dynamic.rule=PathPrefix(`/dynamic/whoami`)
|
||||
# - traefik.http.routers.whoami-dynamic.middlewares=dynamic@docker
|
||||
```
|
||||
|
||||
*dynamic-config.yaml*
|
||||
```yaml
|
||||
http:
|
||||
services:
|
||||
whoami:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://whoami:80"
|
||||
|
||||
routers:
|
||||
whoami-dynamic:
|
||||
rule: PathPrefix(`/dynamic/whoami`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- dynamic@docker
|
||||
service: "whoami"
|
||||
```
|
||||
|
||||
## Traefik with Docker Swarm
|
||||
|
||||
⚠️ Limitations
|
||||
|
||||
- Traefik will evict the service from its pool as soon as the service is 0/0. You must add the [`traefik.docker.lbswarm`](https://doc.traefik.io/traefik/routing/providers/docker/#traefikdockerlbswarm) label.
|
||||
```yaml
|
||||
services:
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
deploy:
|
||||
replicas: 0
|
||||
labels:
|
||||
- traefik.docker.lbswarm=true
|
||||
```
|
||||
- We cannot use [allowEmptyServices](https://doc.traefik.io/traefik/providers/docker/#allowemptyservices) because if you use the [blocking strategy](LINKHERE) you will receive a `503`.
|
||||
|
||||
## Traefik with Kubernetes
|
||||
|
||||
- The format of the `names` section is `<KIND>_<NAMESPACE>_<NAME>_<REPLICACOUNT>` where `_` is the delimiter.
|
||||
- Thus no `_` are allowed in `<NAME>`
|
||||
- `KIND` can be either `deployment` or `statefulset`
|
||||
|
||||
⚠️ Limitations
|
||||
|
||||
- Traefik will evict the service from its pool as soon as there is no endpoint available. You must use [`allowEmptyServices`](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#allowemptyservices)
|
||||
- Blocking Strategy is not yet supported because of how Traefik handles the pod ip.
|
||||
|
||||
See [Kubernetes E2E Traefik Test script](./e2e/kubernetes.sh) to see how it is reproduced
|
||||
|
||||
## Plugin
|
||||
|
||||
The plugin is available in the Traefik [Plugin Catalog](https://plugins.traefik.io/plugins/633b4658a4caa9ddeffda119/sablier)
|
||||
|
||||
## Development
|
||||
|
||||
You can use this to load the plugin.
|
||||
|
||||
```yaml
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:2.9.1
|
||||
command:
|
||||
- --experimental.localPlugins.sablier.moduleName=github.com/sablierapp/sablier
|
||||
- --entryPoints.http.address=:80
|
||||
- --providers.docker=true
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
- '../../..:/plugins-local/src/github.com/sablierapp/sablier'
|
||||
- './dynamic-config.yml:/etc/traefik/dynamic-config.yml'
|
||||
```
|
||||
|
||||
But I recommend you to use the [`e2e`](./e2e/) folder.
|
||||
@@ -1,178 +0,0 @@
|
||||
package traefik
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DynamicConfiguration struct {
|
||||
DisplayName string `yaml:"displayname"`
|
||||
ShowDetails *bool `yaml:"showDetails"`
|
||||
Theme string `yaml:"theme"`
|
||||
RefreshFrequency string `yaml:"refreshFrequency"`
|
||||
}
|
||||
|
||||
type BlockingConfiguration struct {
|
||||
Timeout string `yaml:"timeout"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
SablierURL string `yaml:"sablierUrl"`
|
||||
Names string `yaml:"names"`
|
||||
Group string `yaml:"group"`
|
||||
SessionDuration string `yaml:"sessionDuration"`
|
||||
splittedNames []string
|
||||
Dynamic *DynamicConfiguration `yaml:"dynamic"`
|
||||
Blocking *BlockingConfiguration `yaml:"blocking"`
|
||||
}
|
||||
|
||||
func CreateConfig() *Config {
|
||||
return &Config{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "",
|
||||
Group: "",
|
||||
SessionDuration: "",
|
||||
splittedNames: []string{},
|
||||
Dynamic: nil,
|
||||
Blocking: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) BuildRequest(middlewareName string) (*http.Request, error) {
|
||||
|
||||
if len(c.SablierURL) == 0 {
|
||||
return nil, fmt.Errorf("sablierURL cannot be empty")
|
||||
}
|
||||
|
||||
names := strings.Split(c.Names, ",")
|
||||
for i := range names {
|
||||
names[i] = strings.TrimSpace(names[i])
|
||||
}
|
||||
|
||||
if len(names) >= 1 && len(names[0]) > 0 {
|
||||
c.splittedNames = names
|
||||
}
|
||||
|
||||
if len(names) == 0 && len(c.Group) == 0 {
|
||||
return nil, fmt.Errorf("you must specify at least one name or a group")
|
||||
}
|
||||
|
||||
if c.Dynamic != nil && c.Blocking != nil {
|
||||
return nil, fmt.Errorf("only supply one strategy: dynamic or blocking")
|
||||
}
|
||||
|
||||
if c.Dynamic != nil {
|
||||
return c.buildDynamicRequest(middlewareName)
|
||||
} else if c.Blocking != nil {
|
||||
return c.buildBlockingRequest()
|
||||
}
|
||||
return nil, fmt.Errorf("no strategy configured")
|
||||
}
|
||||
|
||||
func (c *Config) buildDynamicRequest(middlewareName string) (*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 != "" {
|
||||
_, err = time.ParseDuration(c.SessionDuration)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing dynamic.sessionDuration: %v", err)
|
||||
}
|
||||
|
||||
q.Add("session_duration", c.SessionDuration)
|
||||
}
|
||||
|
||||
for _, name := range c.splittedNames {
|
||||
q.Add("names", name)
|
||||
}
|
||||
|
||||
if c.Group != "" {
|
||||
q.Add("group", c.Group)
|
||||
}
|
||||
|
||||
if c.Dynamic.DisplayName != "" {
|
||||
q.Add("display_name", c.Dynamic.DisplayName)
|
||||
} else {
|
||||
// display name defaults as middleware name
|
||||
q.Add("display_name", middlewareName)
|
||||
}
|
||||
|
||||
if c.Dynamic.Theme != "" {
|
||||
q.Add("theme", c.Dynamic.Theme)
|
||||
}
|
||||
|
||||
if c.Dynamic.RefreshFrequency != "" {
|
||||
_, err := time.ParseDuration(c.Dynamic.RefreshFrequency)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing dynamic.refreshFrequency: %v", err)
|
||||
}
|
||||
|
||||
q.Add("refresh_frequency", c.Dynamic.RefreshFrequency)
|
||||
}
|
||||
|
||||
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 != "" {
|
||||
_, err = time.ParseDuration(c.SessionDuration)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing dynamic.sessionDuration: %v", err)
|
||||
}
|
||||
|
||||
q.Add("session_duration", c.SessionDuration)
|
||||
}
|
||||
|
||||
for _, name := range c.splittedNames {
|
||||
q.Add("names", name)
|
||||
}
|
||||
|
||||
if c.Group != "" {
|
||||
q.Add("group", c.Group)
|
||||
}
|
||||
|
||||
if c.Blocking.Timeout != "" {
|
||||
_, err := time.ParseDuration(c.Blocking.Timeout)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error paring blocking.timeout: %v", err)
|
||||
}
|
||||
|
||||
q.Add("timeout", c.Blocking.Timeout)
|
||||
}
|
||||
|
||||
request.URL.RawQuery = q.Encode()
|
||||
|
||||
return request, nil
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
package traefik_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/sablierapp/sablier/plugins/traefik"
|
||||
)
|
||||
|
||||
var fals bool = false
|
||||
var tru bool = true
|
||||
|
||||
func TestConfig_BuildRequest(t *testing.T) {
|
||||
type fields struct {
|
||||
SablierURL string
|
||||
Names string
|
||||
Group string
|
||||
SessionDuration string
|
||||
Dynamic *traefik.DynamicConfiguration
|
||||
Blocking *traefik.BlockingConfiguration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *http.Request
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "dynamic session with required values",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
Dynamic: &traefik.DynamicConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&names=nginx&names=apache", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with default values",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&names=nginx&names=apache&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with group",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "default",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&group=default&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with theme values",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{
|
||||
Theme: "hacker-terminal",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&names=nginx&names=apache&session_duration=1m&theme=hacker-terminal", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with theme and display name values",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.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 invalid session duration",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "invalid",
|
||||
Dynamic: &traefik.DynamicConfiguration{},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with refresh frequency",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{
|
||||
RefreshFrequency: "1m",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&names=nginx&names=apache&refresh_frequency=1m&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with invalid refresh frequency",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{
|
||||
RefreshFrequency: "invalid",
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "dynamic session with show details to true",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{
|
||||
ShowDetails: &tru,
|
||||
RefreshFrequency: "1m",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&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: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{
|
||||
ShowDetails: &fals,
|
||||
RefreshFrequency: "1m",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&names=nginx&names=apache&refresh_frequency=1m&session_duration=1m&show_details=false", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "dynamic session without show details set",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{
|
||||
ShowDetails: nil,
|
||||
RefreshFrequency: "1m",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/dynamic?display_name=sablier-middleware&names=nginx&names=apache&refresh_frequency=1m&session_duration=1m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with required values",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
Blocking: &traefik.BlockingConfiguration{},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with default values",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Blocking: &traefik.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: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Group: "default",
|
||||
SessionDuration: "1m",
|
||||
Blocking: &traefik.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: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Blocking: &traefik.BlockingConfiguration{
|
||||
Timeout: "5m",
|
||||
},
|
||||
},
|
||||
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache&session_duration=1m&timeout=5m", nil),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "blocking session with invalid timeout value",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Blocking: &traefik.BlockingConfiguration{
|
||||
Timeout: "invalid",
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "both strategies defined",
|
||||
fields: fields{
|
||||
SablierURL: "http://sablier:10000",
|
||||
Names: "nginx , apache",
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &traefik.DynamicConfiguration{},
|
||||
Blocking: &traefik.BlockingConfiguration{},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &traefik.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("sablier-middleware")
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Config.BuildRequest() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Config.BuildRequest() = %v, want %v", 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
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.1.4
|
||||
command:
|
||||
- --experimental.localPlugins.sablier.moduleName=github.com/sablierapp/sablier
|
||||
- --entryPoints.http.address=:80
|
||||
- --providers.docker=true
|
||||
- --providers.file.filename=/etc/traefik/dynamic-config.yml
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
- '../../../..:/plugins-local/src/github.com/sablierapp/sablier'
|
||||
- './dynamic-config.yml:/etc/traefik/dynamic-config.yml'
|
||||
restart: "no"
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=docker
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
# Cannot use labels because as soon as the container is stopped, the labels are not treated by Traefik
|
||||
# The route doesn't exist anymore. Use dynamic-config.yml file instead.
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
# - traefik.enable
|
||||
# - traefik.http.routers.whoami.rule=PathPrefix(`/whoami`)
|
||||
# - traefik.http.routers.whoami.middlewares=ondemand
|
||||
|
||||
nginx:
|
||||
image: nginx:1.27.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
# Cannot use labels because as soon as the container is stopped, the labels are not treated by Traefik
|
||||
# The route doesn't exist anymore. Use dynamic-config.yml file instead.
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
# - traefik.enable
|
||||
# - traefik.http.routers.nginx.rule=PathPrefix(`/nginx`)
|
||||
# - traefik.http.routers.nginx.middlewares=ondemand
|
||||
@@ -1,103 +0,0 @@
|
||||
http:
|
||||
services:
|
||||
whoami:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://whoami:80"
|
||||
nginx:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://nginx:80"
|
||||
|
||||
routers:
|
||||
whoami-dynamic:
|
||||
rule: PathPrefix(`/dynamic/whoami`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- dynamic@file
|
||||
service: "whoami"
|
||||
|
||||
whoami-blocking:
|
||||
rule: PathPrefix(`/blocking/whoami`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- blocking@file
|
||||
service: "whoami"
|
||||
|
||||
whoami-multiple:
|
||||
rule: PathPrefix(`/multiple/whoami`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- multiple@file
|
||||
service: "whoami"
|
||||
|
||||
nginx-multiple:
|
||||
rule: PathPrefix(`/multiple/nginx`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- multiple@file
|
||||
service: "nginx"
|
||||
|
||||
nginx-healthy:
|
||||
rule: PathPrefix(`/healthy/nginx`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- healthy@file
|
||||
service: "nginx"
|
||||
|
||||
group:
|
||||
rule: PathPrefix(`/group`)
|
||||
entryPoints:
|
||||
- "http"
|
||||
middlewares:
|
||||
- group@file
|
||||
service: "whoami"
|
||||
|
||||
middlewares:
|
||||
dynamic:
|
||||
plugin:
|
||||
sablier:
|
||||
names: docker_classic_e2e-whoami-1
|
||||
sablierUrl: http://sablier:10000
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: Dynamic Whoami
|
||||
theme: hacker-terminal
|
||||
blocking:
|
||||
plugin:
|
||||
sablier:
|
||||
names: docker_classic_e2e-whoami-1
|
||||
sablierUrl: http://sablier:10000
|
||||
sessionDuration: 1m
|
||||
blocking:
|
||||
timeout: 30s
|
||||
multiple:
|
||||
plugin:
|
||||
sablier:
|
||||
names: docker_classic_e2e-whoami-1,docker_classic_e2e-nginx-1
|
||||
sablierUrl: http://sablier:10000
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: Multiple Whoami
|
||||
theme: hacker-terminal
|
||||
healthy:
|
||||
plugin:
|
||||
sablier:
|
||||
names: docker_classic_e2e-nginx-1
|
||||
sablierUrl: http://sablier:10000
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: Healthy Nginx
|
||||
group:
|
||||
plugin:
|
||||
sablier:
|
||||
group: E2E
|
||||
sablierUrl: http://sablier:10000
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: Group E2E
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
docker compose -f ${DOCKER_COMPOSE_FILE} -p ${DOCKER_COMPOSE_PROJECT_NAME} logs sablier traefik
|
||||
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
|
||||
run_docker_classic_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,112 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.0.4
|
||||
command:
|
||||
- --experimental.localPlugins.sablier.moduleName=github.com/sablierapp/sablier
|
||||
- --entryPoints.http.address=:80
|
||||
- --providers.swarm=true
|
||||
- --providers.swarm.refreshSeconds=1 # Default is 15s
|
||||
- --providers.swarm.allowemptyservices=true
|
||||
ports:
|
||||
- target: 80
|
||||
published: 8080
|
||||
protocol: tcp
|
||||
mode: host # Won't work in github actions otherwise
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
- '../../../..:/plugins-local/src/github.com/sablierapp/sablier'
|
||||
deploy:
|
||||
labels:
|
||||
- traefik.http.services.traefik.loadbalancer.server.port=8080
|
||||
restart_policy:
|
||||
condition: none # Do not restart on setup failure
|
||||
|
||||
sablier:
|
||||
image: sablierapp/sablier:local
|
||||
command:
|
||||
- start
|
||||
- --provider.name=swarm
|
||||
- --logging.level=debug
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
deploy:
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
# Dynamic Middleware
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.names=DOCKER_SWARM_E2E_whoami
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.sablierUrl=http://tasks.sablier:10000
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.sessionDuration=1m
|
||||
- traefik.http.middlewares.dynamic.plugin.sablier.dynamic.displayName=Dynamic Whoami
|
||||
# Blocking Middleware
|
||||
- traefik.http.middlewares.blocking.plugin.sablier.names=DOCKER_SWARM_E2E_whoami
|
||||
- traefik.http.middlewares.blocking.plugin.sablier.sablierUrl=http://tasks.sablier:10000
|
||||
- traefik.http.middlewares.blocking.plugin.sablier.sessionDuration=1m
|
||||
- traefik.http.middlewares.blocking.plugin.sablier.blocking.timeout=30s
|
||||
# Multiple Dynamic Middleware
|
||||
- traefik.http.middlewares.multiple.plugin.sablier.names=DOCKER_SWARM_E2E_whoami,DOCKER_SWARM_E2E_nginx
|
||||
- traefik.http.middlewares.multiple.plugin.sablier.sablierUrl=http://tasks.sablier:10000
|
||||
- traefik.http.middlewares.multiple.plugin.sablier.sessionDuration=1m
|
||||
- traefik.http.middlewares.multiple.plugin.sablier.dynamic.displayName=Multiple Whoami
|
||||
# Healthy Middleware
|
||||
- traefik.http.middlewares.healthy.plugin.sablier.names=DOCKER_SWARM_E2E_nginx
|
||||
- traefik.http.middlewares.healthy.plugin.sablier.sablierUrl=http://tasks.sablier:10000
|
||||
- traefik.http.middlewares.healthy.plugin.sablier.sessionDuration=1m
|
||||
- traefik.http.middlewares.healthy.plugin.sablier.dynamic.displayName=Healthy Nginx
|
||||
# Group Middleware
|
||||
- traefik.http.middlewares.group.plugin.sablier.group=E2E
|
||||
- traefik.http.middlewares.group.plugin.sablier.sablierUrl=http://tasks.sablier:10000
|
||||
- traefik.http.middlewares.group.plugin.sablier.sessionDuration=1m
|
||||
- traefik.http.middlewares.group.plugin.sablier.dynamic.displayName=Group E2E
|
||||
- traefik.http.services.sablier.loadbalancer.server.port=10000
|
||||
|
||||
whoami:
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-f", "http://localhost" ]
|
||||
interval: 5s
|
||||
deploy:
|
||||
replicas: 0
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
- traefik.enable=true
|
||||
# If you do not use the swarm load balancer, traefik will evict the service from its pool
|
||||
# as soon as the service is 0/0. If you do not set that, fallback to dynamic-config.yml file usage.
|
||||
- traefik.docker.lbswarm=true
|
||||
- traefik.http.routers.whoami-dynamic.middlewares=dynamic@swarm
|
||||
- traefik.http.routers.whoami-dynamic.rule=PathPrefix(`/dynamic/whoami`)
|
||||
- traefik.http.routers.whoami-dynamic.service=whoami
|
||||
- traefik.http.routers.whoami-blocking.middlewares=blocking@swarm
|
||||
- traefik.http.routers.whoami-blocking.rule=PathPrefix(`/blocking/whoami`)
|
||||
- traefik.http.routers.whoami-blocking.service=whoami
|
||||
- traefik.http.routers.whoami-multiple.middlewares=multiple@swarm
|
||||
- traefik.http.routers.whoami-multiple.rule=PathPrefix(`/multiple/whoami`)
|
||||
- traefik.http.routers.whoami-multiple.service=whoami
|
||||
- traefik.http.routers.whoami-group.middlewares=group@swarm
|
||||
- traefik.http.routers.whoami-group.rule=PathPrefix(`/group`)
|
||||
- traefik.http.routers.whoami-group.service=whoami
|
||||
- traefik.http.services.whoami.loadbalancer.server.port=80
|
||||
|
||||
nginx:
|
||||
image: nginx:1.23.1
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
interval: 5s
|
||||
deploy:
|
||||
replicas: 0
|
||||
labels:
|
||||
- sablier.enable=true
|
||||
- sablier.group=E2E
|
||||
- traefik.enable=true
|
||||
# If you do not use the swarm load balancer, traefik will evict the service from its pool
|
||||
# as soon as the service is 0/0. If you do not set that, fallback to dynamic-config.yml file usage.
|
||||
- traefik.docker.lbswarm=true
|
||||
- traefik.http.routers.nginx-multiple.middlewares=multiple@swarm
|
||||
- traefik.http.routers.nginx-multiple.rule=PathPrefix(`/multiple/nginx`)
|
||||
- traefik.http.routers.nginx-multiple.service=nginx
|
||||
- traefik.http.routers.nginx-healthy.middlewares=healthy@swarm
|
||||
- traefik.http.routers.nginx-healthy.rule=PathPrefix(`/healthy/nginx`)
|
||||
- traefik.http.routers.nginx-healthy.service=nginx
|
||||
- traefik.http.services.nginx.loadbalancer.server.port=80
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
docker service logs ${DOCKER_STACK_NAME}_sablier
|
||||
docker service logs ${DOCKER_STACK_NAME}_traefik
|
||||
fi
|
||||
destroy_docker_stack
|
||||
}
|
||||
|
||||
trap destroy_docker_swarm EXIT
|
||||
|
||||
prepare_docker_swarm
|
||||
run_docker_swarm_test Test_Dynamic
|
||||
run_docker_swarm_test Test_Blocking
|
||||
run_docker_swarm_test Test_Multiple
|
||||
run_docker_swarm_test Test_Healthy
|
||||
run_docker_swarm_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,25 +0,0 @@
|
||||
version: '3'
|
||||
services:
|
||||
server:
|
||||
image: "rancher/k3s:v1.30.2-k3s1"
|
||||
command: server --disable=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/sablierapp/sablier'
|
||||
ports:
|
||||
- 6443:6443 # Kubernetes API Server
|
||||
- 8080:80 # Ingress controller port 80
|
||||
@@ -1,272 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami-deployment
|
||||
labels:
|
||||
app: whoami
|
||||
sablier.enable: "true"
|
||||
sablier.group: "E2E"
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: acouvreur/whoami:v1.10.2
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-service
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: dynamic
|
||||
namespace: default
|
||||
spec:
|
||||
plugin:
|
||||
sablier:
|
||||
names: deployment_default_whoami-deployment_1
|
||||
sablierUrl: 'http://sablier:10000'
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: 'Dynamic Whoami'
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: blocking
|
||||
namespace: default
|
||||
spec:
|
||||
plugin:
|
||||
sablier:
|
||||
names: deployment_default_whoami-deployment_1
|
||||
sablierUrl: 'http://sablier:10000'
|
||||
sessionDuration: 1m
|
||||
blocking:
|
||||
timeout: 30s
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: multiple
|
||||
namespace: default
|
||||
spec:
|
||||
plugin:
|
||||
sablier:
|
||||
names: deployment_default_whoami-deployment_1,deployment_default_nginx-deployment_1
|
||||
sablierUrl: 'http://sablier:10000'
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: 'Multiple Whoami'
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: group
|
||||
namespace: default
|
||||
spec:
|
||||
plugin:
|
||||
sablier:
|
||||
group: E2E
|
||||
sablierUrl: 'http://sablier:10000'
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: 'Group E2E'
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: whoami-dynamic-ingress
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-dynamic@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /dynamic/whoami
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: whoami-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: whoami-blocking-ingress
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-blocking@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /blocking/whoami
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: whoami-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: whoami-multiple-ingress
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-multiple@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /multiple/whoami
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: whoami-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
sablier.enable: "true"
|
||||
sablier.group: "E2E"
|
||||
spec:
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.29.3
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- curl
|
||||
- -f
|
||||
- http://localhost
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
targetPort: 80
|
||||
port: 80
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: healthy
|
||||
namespace: default
|
||||
spec:
|
||||
plugin:
|
||||
sablier:
|
||||
names: deployment_default_nginx-deployment_1
|
||||
sablierUrl: 'http://sablier:10000'
|
||||
sessionDuration: 1m
|
||||
dynamic:
|
||||
displayName: 'Healthy Nginx'
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-multiple-ingress
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-multiple@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /multiple/nginx
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-healthy-ingress
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-healthy@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /healthy/nginx
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: group-ingress
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-group@kubernetescrd
|
||||
spec:
|
||||
rules:
|
||||
- host: localhost
|
||||
http:
|
||||
paths:
|
||||
- path: /group
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: whoami-service
|
||||
port:
|
||||
number: 80
|
||||
@@ -1,78 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sablier-deployment
|
||||
namespace: kube-system
|
||||
labels:
|
||||
app: sablier
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sablier
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sablier
|
||||
spec:
|
||||
serviceAccountName: sablier
|
||||
containers:
|
||||
- name: sablier
|
||||
image: sablierapp/sablier:local
|
||||
args: ["start", "--provider.name=kubernetes", "--logging.level=debug"]
|
||||
ports:
|
||||
- containerPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
app: sablier
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 10000
|
||||
targetPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- apps
|
||||
- ""
|
||||
resources:
|
||||
- deployments
|
||||
- deployments/scale
|
||||
- statefulsets
|
||||
- statefulsets/scale
|
||||
verbs:
|
||||
- patch # Scale up and down
|
||||
- get # Retrieve info about specific dep
|
||||
- update # Scale up and down
|
||||
- list # Events
|
||||
- watch # Events
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: sablier
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: sablier
|
||||
namespace: kube-system
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/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 sablierapp/sablier:local into k3s..."
|
||||
docker save sablierapp/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_traefik() {
|
||||
helm repo add traefik https://traefik.github.io/charts
|
||||
helm repo update
|
||||
helm install traefik --version 28.3.0 traefik/traefik -f values.yaml --namespace kube-system
|
||||
}
|
||||
|
||||
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/sablierapp/sablier/e2e; then
|
||||
errors=1
|
||||
kubectl -n kube-system logs deployments/sablier-deployment
|
||||
kubectl -n kube-system logs deployments/traefik
|
||||
fi
|
||||
|
||||
destroy_deployment
|
||||
}
|
||||
|
||||
trap destroy_kubernetes EXIT
|
||||
|
||||
prepare_kubernetes
|
||||
prepare_traefik
|
||||
run_kubernetes_deployment_test Test_Dynamic
|
||||
run_kubernetes_deployment_test Test_Blocking
|
||||
run_kubernetes_deployment_test Test_Multiple
|
||||
run_kubernetes_deployment_test Test_Healthy
|
||||
run_kubernetes_deployment_test Test_Group
|
||||
|
||||
exit $errors
|
||||
@@ -1,21 +0,0 @@
|
||||
additionalArguments:
|
||||
- "--experimental.localPlugins.sablier.moduleName=github.com/sablierapp/sablier"
|
||||
|
||||
providers:
|
||||
kubernetesIngress:
|
||||
allowEmptyServices: true
|
||||
kubernetesCRD:
|
||||
allowEmptyServices: true
|
||||
|
||||
additionalVolumeMounts:
|
||||
- name: local-sablier-plugin
|
||||
mountPath: /plugins-local/src/github.com/sablierapp/sablier
|
||||
|
||||
deployment:
|
||||
additionalVolumes:
|
||||
- name: local-sablier-plugin
|
||||
hostPath:
|
||||
# directory location on host
|
||||
path: /plugins-local/src/github.com/sablierapp/sablier
|
||||
# this field is optional
|
||||
type: Directory
|
||||
@@ -1,3 +0,0 @@
|
||||
module github.com/sablierapp/sablier/plugins/traefik
|
||||
|
||||
go 1.24.0
|
||||
@@ -1,146 +0,0 @@
|
||||
package traefik
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
)
|
||||
|
||||
type SablierMiddleware struct {
|
||||
client *http.Client
|
||||
request *http.Request
|
||||
next http.Handler
|
||||
useRedirect bool
|
||||
}
|
||||
|
||||
// New function creates the configuration
|
||||
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
|
||||
req, err := config.BuildRequest(name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SablierMiddleware{
|
||||
request: req,
|
||||
client: &http.Client{},
|
||||
next: next,
|
||||
// there is no way to make blocking work in traefik without redirect so let's make it default
|
||||
useRedirect: config.Blocking != nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sm *SablierMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
sablierRequest := sm.request.Clone(context.TODO())
|
||||
|
||||
resp, err := sm.client.Do(sablierRequest)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
conditonalResponseWriter := newResponseWriter(rw)
|
||||
|
||||
useRedirect := false
|
||||
|
||||
if resp.Header.Get("X-Sablier-Session-Status") == "ready" {
|
||||
// Check if the backend already received request data
|
||||
trace := &httptrace.ClientTrace{
|
||||
WroteHeaders: func() {
|
||||
conditonalResponseWriter.ready = true
|
||||
},
|
||||
WroteRequest: func(info httptrace.WroteRequestInfo) {
|
||||
conditonalResponseWriter.ready = true
|
||||
},
|
||||
}
|
||||
newCtx := httptrace.WithClientTrace(req.Context(), trace)
|
||||
sm.next.ServeHTTP(conditonalResponseWriter, req.WithContext(newCtx))
|
||||
useRedirect = sm.useRedirect
|
||||
}
|
||||
|
||||
if conditonalResponseWriter.ready == false {
|
||||
conditonalResponseWriter.ready = true
|
||||
if useRedirect {
|
||||
conditonalResponseWriter.Header().Set("Location", req.URL.String())
|
||||
|
||||
status := http.StatusFound
|
||||
if req.Method != http.MethodGet {
|
||||
status = http.StatusTemporaryRedirect
|
||||
}
|
||||
|
||||
conditonalResponseWriter.WriteHeader(status)
|
||||
_, err := conditonalResponseWriter.Write([]byte(http.StatusText(status)))
|
||||
if err != nil {
|
||||
http.Error(conditonalResponseWriter, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
} else {
|
||||
conditonalResponseWriter.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
|
||||
io.Copy(conditonalResponseWriter, resp.Body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newResponseWriter(rw http.ResponseWriter) *responseWriter {
|
||||
return &responseWriter{
|
||||
responseWriter: rw,
|
||||
headers: make(http.Header),
|
||||
}
|
||||
}
|
||||
|
||||
type responseWriter struct {
|
||||
responseWriter http.ResponseWriter
|
||||
headers http.Header
|
||||
ready bool
|
||||
}
|
||||
|
||||
func (r *responseWriter) Header() http.Header {
|
||||
if r.ready {
|
||||
return r.responseWriter.Header()
|
||||
}
|
||||
return r.headers
|
||||
}
|
||||
|
||||
func (r *responseWriter) Write(buf []byte) (int, error) {
|
||||
if r.ready == false {
|
||||
return len(buf), nil
|
||||
}
|
||||
return r.responseWriter.Write(buf)
|
||||
}
|
||||
|
||||
func (r *responseWriter) WriteHeader(code int) {
|
||||
if r.ready == false && code == http.StatusServiceUnavailable {
|
||||
// We get a 503 HTTP Status Code when there is no backend server in the pool
|
||||
// to which the request could be sent. Also, note that r.ready
|
||||
// will never return false in case there was a connection established to
|
||||
// the backend server and so we can be sure that the 503 was produced
|
||||
// inside Traefik already
|
||||
return
|
||||
}
|
||||
|
||||
headers := r.responseWriter.Header()
|
||||
for header, value := range r.headers {
|
||||
headers[header] = value
|
||||
}
|
||||
|
||||
r.responseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (r *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hijacker, ok := r.responseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", r.responseWriter)
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
func (r *responseWriter) Flush() {
|
||||
if flusher, ok := r.responseWriter.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
package traefik
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httptrace"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSablierMiddleware_ServeHTTP(t *testing.T) {
|
||||
type fields struct {
|
||||
Next http.Handler
|
||||
Config *Config
|
||||
}
|
||||
type sablier struct {
|
||||
headers map[string]string
|
||||
body string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
sablier sablier
|
||||
expected string
|
||||
code int
|
||||
}{
|
||||
{
|
||||
name: "sablier service is ready",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
httptrace.ContextClientTrace(r.Context()).WroteHeaders()
|
||||
fmt.Fprint(w, "response from service")
|
||||
|
||||
}),
|
||||
Config: &Config{
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &DynamicConfiguration{},
|
||||
},
|
||||
},
|
||||
expected: "response from service",
|
||||
code: 200,
|
||||
},
|
||||
{
|
||||
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: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
httptrace.ContextClientTrace(r.Context()).WroteHeaders()
|
||||
fmt.Fprint(w, "response from service")
|
||||
}),
|
||||
Config: &Config{
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &DynamicConfiguration{},
|
||||
},
|
||||
},
|
||||
expected: "response from sablier",
|
||||
code: 200,
|
||||
},
|
||||
{
|
||||
name: "sablier service is ready but 503",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}),
|
||||
Config: &Config{
|
||||
SessionDuration: "1m",
|
||||
Dynamic: &DynamicConfiguration{},
|
||||
},
|
||||
},
|
||||
expected: "response from sablier",
|
||||
code: 200,
|
||||
},
|
||||
{
|
||||
name: "sablier service is ready blocking",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
httptrace.ContextClientTrace(r.Context()).WroteHeaders()
|
||||
fmt.Fprint(w, "response from service")
|
||||
}),
|
||||
Config: &Config{
|
||||
SessionDuration: "1m",
|
||||
Blocking: &BlockingConfiguration{},
|
||||
},
|
||||
},
|
||||
expected: "response from service",
|
||||
code: 200,
|
||||
},
|
||||
{
|
||||
name: "sablier service is not ready blocking",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "not-ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
httptrace.ContextClientTrace(r.Context()).WroteHeaders()
|
||||
fmt.Fprint(w, "response from service")
|
||||
}),
|
||||
Config: &Config{
|
||||
SessionDuration: "1m",
|
||||
Blocking: &BlockingConfiguration{},
|
||||
},
|
||||
},
|
||||
expected: "response from sablier",
|
||||
// is this correct for blocking? I would expect to get error
|
||||
code: 200,
|
||||
},
|
||||
{
|
||||
name: "sablier service is ready blocking but 503",
|
||||
sablier: sablier{
|
||||
headers: map[string]string{
|
||||
"X-Sablier-Session-Status": "ready",
|
||||
},
|
||||
body: "response from sablier",
|
||||
},
|
||||
fields: fields{
|
||||
Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}),
|
||||
Config: &Config{
|
||||
SessionDuration: "1m",
|
||||
Blocking: &BlockingConfiguration{},
|
||||
},
|
||||
},
|
||||
expected: "Found",
|
||||
code: 302,
|
||||
},
|
||||
}
|
||||
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.Config.SablierURL = sablierMockServer.URL
|
||||
|
||||
sm, err := New(context.Background(), tt.fields.Next, tt.fields.Config, "middleware")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/my-nginx", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
sm.ServeHTTP(w, req)
|
||||
|
||||
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))
|
||||
}
|
||||
if res.StatusCode != tt.code {
|
||||
t.Errorf("expected '%d' got '%d'", tt.code, res.StatusCode)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
module.exports = {
|
||||
"branches": [
|
||||
{ "name": "main" },
|
||||
{ "name": "beta", "channel": "beta", "prerelease": "beta" },
|
||||
],
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
["@semantic-release/exec", {
|
||||
"publishCmd": "make VERSION=${nextRelease.version} release -j 3 && make VERSION=${nextRelease.version} proxywasm"
|
||||
}],
|
||||
["@semantic-release/github", {
|
||||
"assets": [
|
||||
"sablier*"
|
||||
]
|
||||
}],
|
||||
["@semantic-release/exec", {
|
||||
"prepareCmd": "make LAST=${lastRelease.version} NEXT=${nextRelease.version} update-doc-version update-doc-version-middleware"
|
||||
}],
|
||||
["@semantic-release/git", {
|
||||
"assets": [["**/*.{md,yml}", "!node_modules/**/*.{md,yml}"]],
|
||||
"message": "docs(release): update doc version from ${lastRelease.version} to ${nextRelease.version}"
|
||||
}]
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user