diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index cfe5a10..a1e6d3b 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -80,4 +80,39 @@ jobs: run: docker load --input /tmp/sablier.tar - name: Test ${{ matrix.provider }} - run: cd plugins/traefik/e2e && bash ./${{ matrix.provider }}.sh \ No newline at end of file + run: cd plugins/traefik/e2e && bash ./${{ matrix.provider }}.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_classic, docker_swarm, kubernetes] + steps: + - name: Set up Go 1.18 + uses: actions/setup-go@v2 + with: + go-version: 1.18 + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Download artifact + uses: actions/download-artifact@v2 + with: + name: sablier-image-tar + path: /tmp + + - name: Load Docker image + run: docker load --input /tmp/sablier.tar + + - name: Test ${{ matrix.provider }} + run: cd plugins/nginx/e2e && bash ./${{ matrix.provider }}.sh \ No newline at end of file diff --git a/plugins/nginx/README.md b/plugins/nginx/README.md new file mode 100644 index 0000000..235e605 --- /dev/null +++ b/plugins/nginx/README.md @@ -0,0 +1,90 @@ +# 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 $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/.sh` (docker, kubernetes, etc.) \ No newline at end of file diff --git a/plugins/nginx/e2e/conf/nginx_docker_classic.conf b/plugins/nginx/e2e/conf/nginx_docker_classic.conf new file mode 100644 index 0000000..e7204d6 --- /dev/null +++ b/plugins/nginx/e2e/conf/nginx_docker_classic.conf @@ -0,0 +1,82 @@ +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; + } +} \ No newline at end of file diff --git a/plugins/nginx/e2e/conf/nginx_docker_swarm.conf b/plugins/nginx/e2e/conf/nginx_docker_swarm.conf new file mode 100644 index 0000000..d6800ff --- /dev/null +++ b/plugins/nginx/e2e/conf/nginx_docker_swarm.conf @@ -0,0 +1,82 @@ +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; + } +} \ No newline at end of file diff --git a/plugins/nginx/e2e/docker-compose.yml b/plugins/nginx/e2e/docker-compose.yml new file mode 100644 index 0000000..7783e20 --- /dev/null +++ b/plugins/nginx/e2e/docker-compose.yml @@ -0,0 +1,32 @@ +version: '3.9' + +services: + proxy: + image: nginx:1.23.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 + - ./conf/nginx_docker_classic.conf:/etc/nginx/conf.d/default.conf + + sablier: + image: acouvreur/sablier:local + ports: + - 10000:10000 + command: + - start + - --provider.name=docker + - --logging.level=trace + volumes: + - '/var/run/docker.sock:/var/run/docker.sock' + + whoami: + image: containous/whoami:v1.5.0 + + nginx: + image: nginx:1.23.1 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] + interval: 5s \ No newline at end of file diff --git a/plugins/nginx/e2e/docker-kubernetes.yml b/plugins/nginx/e2e/docker-kubernetes.yml new file mode 100644 index 0000000..fb68599 --- /dev/null +++ b/plugins/nginx/e2e/docker-kubernetes.yml @@ -0,0 +1,25 @@ +version: '3' +services: + server: + image: "rancher/k3s:v1.23.12-k3s1" + command: server --no-deploy traefik + tmpfs: + - /run + - /var/run + ulimits: + nproc: 65535 + nofile: + soft: 65535 + hard: 65535 + privileged: true + restart: always + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + volumes: + # This is just so that we get the kubeconfig file out + - .:/output + - '../../..:/plugins-local/src/github.com/acouvreur/sablier' + ports: + - 6443:6443 # Kubernetes API Server + - 8080:80 # Ingress controller port 80 diff --git a/plugins/nginx/e2e/docker-stack.yml b/plugins/nginx/e2e/docker-stack.yml new file mode 100644 index 0000000..ed0148f --- /dev/null +++ b/plugins/nginx/e2e/docker-stack.yml @@ -0,0 +1,39 @@ +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 + - ./conf/nginx_docker_swarm.conf:/etc/nginx/conf.d/default.conf + + sablier: + image: acouvreur/sablier:local + ports: + - 10000:10000 + command: + - start + - --provider.name=swarm + - --logging.level=trace + volumes: + - '/var/run/docker.sock:/var/run/docker.sock' + + whoami: + image: containous/whoami:v1.5.0 + deploy: + replicas: 0 + + nginx: + image: nginx:1.23.1 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] + interval: 5s + deploy: + replicas: 0 \ No newline at end of file diff --git a/plugins/nginx/e2e/docker_classic.sh b/plugins/nginx/e2e/docker_classic.sh new file mode 100644 index 0000000..3c4717c --- /dev/null +++ b/plugins/nginx/e2e/docker_classic.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +DOCKER_COMPOSE_FILE=docker-compose.yml +DOCKER_COMPOSE_PROJECT_NAME=docker_classic_e2e + +errors=0 + +echo "Using Docker version:" +docker version + +prepare_docker_classic() { + docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME up -d + docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME stop whoami nginx +} + +destroy_docker_classic() { + docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME down --remove-orphans || true +} + +run_docker_classic_test() { + echo "Running Docker Classic Test: $1" + prepare_docker_classic + sleep 2 + go clean -testcache + if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/acouvreur/sablier/e2e; then + errors=1 + docker compose -f ${DOCKER_COMPOSE_FILE} -p ${DOCKER_COMPOSE_PROJECT_NAME} logs sablier proxy + fi + destroy_docker_classic +} + +trap destroy_docker_classic EXIT + +run_docker_classic_test Test_Dynamic +run_docker_classic_test Test_Blocking +run_docker_classic_test Test_Multiple +run_docker_classic_test Test_Healthy + +exit $errors \ No newline at end of file diff --git a/plugins/nginx/e2e/docker_swarm.sh b/plugins/nginx/e2e/docker_swarm.sh new file mode 100644 index 0000000..a101071 --- /dev/null +++ b/plugins/nginx/e2e/docker_swarm.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +DOCKER_STACK_FILE=docker-stack.yml +DOCKER_STACK_NAME=DOCKER_SWARM_E2E + +errors=0 + +echo "Using Docker version:" +docker version + +prepare_docker_swarm() { + docker swarm init +} + +prepare_docker_stack() { + docker stack deploy --compose-file $DOCKER_STACK_FILE ${DOCKER_STACK_NAME} + docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock sudobmitch/docker-stack-wait -t 60 ${DOCKER_STACK_NAME} +} + +destroy_docker_stack() { + docker stack rm ${DOCKER_STACK_NAME} + # Sometimes, the network is not well cleaned up, see https://github.com/moby/moby/issues/30942#issuecomment-540699206 + until [ -z "$(docker stack ps ${DOCKER_STACK_NAME} -q)" ]; do sleep 1; done +} + +destroy_docker_swarm() { + docker swarm leave -f || true +} + +run_docker_swarm_test() { + echo "Running Docker Swarm Test: $1" + prepare_docker_stack + sleep 10 + go clean -testcache + if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/acouvreur/sablier/e2e; then + errors=1 + docker service logs ${DOCKER_STACK_NAME}_sablier + docker service logs ${DOCKER_STACK_NAME}_proxy + fi + destroy_docker_stack +} + +trap destroy_docker_swarm EXIT + +prepare_docker_swarm +run_docker_swarm_test Test_Dynamic +run_docker_swarm_test Test_Blocking +run_docker_swarm_test Test_Multiple +run_docker_swarm_test Test_Healthy + +exit $errors \ No newline at end of file diff --git a/plugins/nginx/e2e/kubernetes.sh b/plugins/nginx/e2e/kubernetes.sh new file mode 100644 index 0000000..68a4310 --- /dev/null +++ b/plugins/nginx/e2e/kubernetes.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +export DOCKER_COMPOSE_FILE=docker-kubernetes.yml +export DOCKER_COMPOSE_PROJECT_NAME=kubernetes_e2e + +errors=0 + +export KUBECONFIG=./kubeconfig.yaml + +echo "Using Docker version:" +docker version + +prepare_kubernetes() { + docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME up -d + until kubectl get nodes | grep " Ready "; do sleep 1; done + echo "Loading acouvreur/sablier:local into k3s..." + docker save acouvreur/sablier:local | docker exec -i ${DOCKER_COMPOSE_PROJECT_NAME}-server-1 ctr images import - + echo "Loading succeeded." +} + +destroy_kubernetes() { + docker compose -f $DOCKER_COMPOSE_FILE -p $DOCKER_COMPOSE_PROJECT_NAME down --volumes +} + + +prepare_deployment() { + kubectl apply -f ./manifests/sablier.yml + kubectl apply -f ./manifests/deployment.yml +} + +destroy_deployment() { + kubectl delete -f ./manifests/deployment.yml + kubectl delete -f ./manifests/sablier.yml +} + +prepare_stateful_set() { + kubectl apply -f ./manifests/statefulset.yml +} + +destroy_stateful_set() { + kubectl delete -f ./manifests/statefulset.yml +} + +run_kubernetes_deployment_test() { + echo "---- Running Kubernetes Test: $1 ----" + prepare_deployment + sleep 10 + go clean -testcache + if ! go test -count=1 -tags e2e -timeout 30s -run ^${1}$ github.com/acouvreur/sablier/e2e; then + errors=1 + kubectl -n kube-system logs deployments/sablier-deployment + # kubectl -n kube-system logs deployments/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 \ No newline at end of file diff --git a/plugins/nginx/e2e/manifests/deployment.yml b/plugins/nginx/e2e/manifests/deployment.yml new file mode 100644 index 0000000..6abfbdd --- /dev/null +++ b/plugins/nginx/e2e/manifests/deployment.yml @@ -0,0 +1,101 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whoami-deployment + labels: + app: whoami +spec: + replicas: 0 + selector: + matchLabels: + app: whoami + template: + metadata: + labels: + app: whoami + spec: + containers: + - name: whoami + image: containous/whoami:v1.5.0 +--- +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 +spec: + replicas: 0 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.23.1 +--- +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 \ No newline at end of file diff --git a/plugins/nginx/e2e/manifests/sablier.yml b/plugins/nginx/e2e/manifests/sablier.yml new file mode 100644 index 0000000..7b8124e --- /dev/null +++ b/plugins/nginx/e2e/manifests/sablier.yml @@ -0,0 +1,79 @@ +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 + serviceAccount: sablier + containers: + - name: sablier + image: acouvreur/sablier:local + args: ["start", "--provider.name=kubernetes", "--logging.level=trace"] + 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 \ No newline at end of file diff --git a/plugins/nginx/e2e/nginx.conf b/plugins/nginx/e2e/nginx.conf new file mode 100644 index 0000000..9b7f7df --- /dev/null +++ b/plugins/nginx/e2e/nginx.conf @@ -0,0 +1,33 @@ +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; +} \ No newline at end of file diff --git a/plugins/nginx/njs/sablier.js b/plugins/nginx/njs/sablier.js new file mode 100644 index 0000000..3afd4b2 --- /dev/null +++ b/plugins/nginx/njs/sablier.js @@ -0,0 +1,104 @@ +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} 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, + 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 = querystring.stringify({ + names: config.names.split(",").map(name => name.trim()), + session_duration: config.sessionDuration, + display_name:config.displayName, + theme: config.theme, + refresh_frequency: config.refreshFrequency, + show_details: config.showDetails + }); + + return {url, query} +} + +/** + * + * @param {SablierConfig} config + * @returns + */ + function createBlockingUrl(config) { + const url = `${config.sablierUrl}/api/strategies/blocking` + const query = querystring.stringify({ + names: config.names.split(",").map(name => name.trim()), + session_duration: config.sessionDuration, + timeout:config.timeout, + }); + + return {url, query} +} + +export default { call }; \ No newline at end of file diff --git a/plugins/traefik/e2e/docker-compose.yml b/plugins/traefik/e2e/docker-compose.yml index e447ef8..ea8375c 100644 --- a/plugins/traefik/e2e/docker-compose.yml +++ b/plugins/traefik/e2e/docker-compose.yml @@ -61,8 +61,7 @@ services: image: nginx:1.23.1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] - start_period: 10s - interval: 1s + 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: diff --git a/plugins/traefik/e2e/docker-stack.yml b/plugins/traefik/e2e/docker-stack.yml index 1529b09..a52419e 100644 --- a/plugins/traefik/e2e/docker-stack.yml +++ b/plugins/traefik/e2e/docker-stack.yml @@ -13,7 +13,7 @@ services: - target: 80 published: 8080 protocol: tcp - mode: host # Use host to bypass ingress, which is not available in codespaces + mode: host # Won't work in github actions otherwise volumes: - '/var/run/docker.sock:/var/run/docker.sock' - '../../..:/plugins-local/src/github.com/acouvreur/sablier' @@ -80,8 +80,7 @@ services: image: nginx:1.23.1 healthcheck: test: ["CMD", "curl", "-f", "http://localhost"] - start_period: 5s - interval: 1s + interval: 5s deploy: replicas: 0 labels: