chore: eetest (#2937)
* chore: Windmill integration tests * Revert github workflow temp changes * fix ee --------- Co-authored-by: gbouv <guillaume@windmill.dev>
This commit is contained in:
25
.github/change-versions-mac.sh
vendored
Executable file
25
.github/change-versions-mac.sh
vendored
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
root_dirpath="$(cd "${script_dirpath}/.." && pwd)"
|
||||
|
||||
VERSION=$1
|
||||
echo "Updating versions to: $VERSION"
|
||||
|
||||
sed -i '' -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/backend/Cargo.toml
|
||||
sed -i '' -e "/^export const VERSION =/s/= .*/= \"v$VERSION\";/" ${root_dirpath}/cli/main.ts
|
||||
sed -i '' -e "/^export const VERSION =/s/= .*/= \"v$VERSION\";/" ${root_dirpath}/benchmarks/lib.ts
|
||||
sed -i '' -e "/version: /s/: .*/: $VERSION/" ${root_dirpath}/backend/windmill-api/openapi.yaml
|
||||
sed -i '' -e "/version: /s/: .*/: $VERSION/" ${root_dirpath}/openflow.openapi.yaml
|
||||
sed -i '' -e "/\"version\": /s/: .*,/: \"$VERSION\",/" ${root_dirpath}/typescript-client/package.json
|
||||
sed -i '' -e "/\"version\": /s/: .*,/: \"$VERSION\",/" ${root_dirpath}/frontend/package.json
|
||||
sed -i '' -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml
|
||||
sed -i '' -e "/^windmill-api =/s/= .*/= \"\\^$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml
|
||||
sed -i '' -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill_pg/pyproject.toml
|
||||
# sed -i '' -e "/^wmill =/s/= .*/= \"\\^$VERSION\"/" python-client/wmill_pg/pyproject.toml
|
||||
sed -i '' -e "/^wmill =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
sed -i '' -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
|
||||
sed -i '' -E "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock
|
||||
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only
|
||||
32
.github/change-versions.sh
vendored
32
.github/change-versions.sh
vendored
@@ -1,23 +1,25 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
root_dirpath="$(cd "${script_dirpath}/.." && pwd)"
|
||||
|
||||
VERSION=$1
|
||||
echo "Updating versions to: $VERSION"
|
||||
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" backend/Cargo.toml
|
||||
sed -i -e "/^export const VERSION =/s/= .*/= \"v$VERSION\";/" cli/main.ts
|
||||
sed -i -e "/^export const VERSION =/s/= .*/= \"v$VERSION\";/" benchmarks/lib.ts
|
||||
sed -i -e "/version: /s/: .*/: $VERSION/" backend/windmill-api/openapi.yaml
|
||||
sed -i -e "/version: /s/: .*/: $VERSION/" openflow.openapi.yaml
|
||||
sed -i -e "/\"version\": /s/: .*,/: \"$VERSION\",/" typescript-client/package.json
|
||||
sed -i -e "/\"version\": /s/: .*,/: \"$VERSION\",/" frontend/package.json
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" python-client/wmill/pyproject.toml
|
||||
sed -i -e "/^windmill-api =/s/= .*/= \"\\^$VERSION\"/" python-client/wmill/pyproject.toml
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" python-client/wmill_pg/pyproject.toml
|
||||
# sed -i -e "/^wmill =/s/= .*/= \"\\^$VERSION\"/" python-client/wmill_pg/pyproject.toml
|
||||
sed -i -e "/^wmill =/s/= .*/= \">=$VERSION\"/" lsp/Pipfile
|
||||
sed -i -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" lsp/Pipfile
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/backend/Cargo.toml
|
||||
sed -i -e "/^export const VERSION =/s/= .*/= \"v$VERSION\";/" ${root_dirpath}/cli/main.ts
|
||||
sed -i -e "/^export const VERSION =/s/= .*/= \"v$VERSION\";/" ${root_dirpath}/benchmarks/lib.ts
|
||||
sed -i -e "/version: /s/: .*/: $VERSION/" ${root_dirpath}/backend/windmill-api/openapi.yaml
|
||||
sed -i -e "/version: /s/: .*/: $VERSION/" ${root_dirpath}/openflow.openapi.yaml
|
||||
sed -i -e "/\"version\": /s/: .*,/: \"$VERSION\",/" ${root_dirpath}/typescript-client/package.json
|
||||
sed -i -e "/\"version\": /s/: .*,/: \"$VERSION\",/" ${root_dirpath}/frontend/package.json
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml
|
||||
sed -i -e "/^windmill-api =/s/= .*/= \"\\^$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml
|
||||
sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill_pg/pyproject.toml
|
||||
# sed -i -e "/^wmill =/s/= .*/= \"\\^$VERSION\"/" ${root_dirpath}/python-client/wmill_pg/pyproject.toml
|
||||
sed -i -e "/^wmill =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
sed -i -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile
|
||||
|
||||
sed -i -zE "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" backend/Cargo.lock
|
||||
sed -i -zE "s/name = \"windmill\"\nversion = \"[^\"]*\"\\n(.*)/name = \"windmill\"\nversion = \"$VERSION\"\\n\\1/" ${root_dirpath}/backend/Cargo.lock
|
||||
|
||||
cd frontend && npm i --package-lock-only
|
||||
cd ${root_dirpath}/frontend && npm i --package-lock-only
|
||||
|
||||
58
.github/workflows/docker-image.yml
vendored
58
.github/workflows/docker-image.yml
vendored
@@ -43,6 +43,8 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
@@ -56,7 +58,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
labels: |
|
||||
${{ steps.meta-public.outputs.labels }}
|
||||
@@ -79,6 +81,8 @@ jobs:
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
@@ -102,7 +106,7 @@ jobs:
|
||||
features=enterprise,enterprise_saml
|
||||
nsjail=true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:latest
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
labels: |
|
||||
${{ steps.meta-ee-public.outputs.labels }}
|
||||
@@ -322,3 +326,53 @@ jobs:
|
||||
folder: ${{ steps.extract.outputs.destination }}
|
||||
bucket: windmill-frontend
|
||||
bucket-region: us-east-1
|
||||
|
||||
run_integration_test:
|
||||
runs-on: ubicloud
|
||||
needs: [build_ee]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Prepare test run
|
||||
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}
|
||||
run: cd integration_tests && ./build.sh
|
||||
- name: Test run
|
||||
if: ${{ ! startsWith(github.ref, 'refs/tags/') }}
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
LICENSE_KEY: ${{ secrets.WM_LICENSE_KEY_CI }}
|
||||
run: cd integration_tests && ./run.sh
|
||||
- name: Archive logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: Windmill Integration Tests Logs
|
||||
path: |
|
||||
integration_tests/logs
|
||||
|
||||
|
||||
tag_latest:
|
||||
runs-on: ubicloud
|
||||
needs: [run_integration_test]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Pull dev
|
||||
run:
|
||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev
|
||||
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
|
||||
docker push -a ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev
|
||||
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:dev ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
|
||||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee
|
||||
|
||||
4
integration_tests/.gitignore
vendored
Normal file
4
integration_tests/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
!.env
|
||||
*/__pycache__/
|
||||
.venv/
|
||||
logs/
|
||||
45
integration_tests/README.md
Normal file
45
integration_tests/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
Windmill integration tests
|
||||
==========================
|
||||
|
||||
Integration that are run on every push to the main branch (excluding tags)
|
||||
|
||||
The concept is the following:
|
||||
1. It pulls the latest published image with the version from (version.txt)[../version.txt] and deploys a Windmill stack using the (docker-compose.yml)[docker-compose.yml]
|
||||
2. It runs the tests using python testing framework (`python -m unittest -v test`). All test classes exported in (__init__.py)[./test/__init__py] will be run
|
||||
3. Then it upgrades the stack to the latest (unpublished) version of Windmill built for this commit. It expects a docker image with the tag `dev` to be present.
|
||||
4. It re-runs the tests with the following env var: `WMILL_RUNNING_DEV` set to `1`
|
||||
|
||||
Some tests behaves differently depending on `WMILL_RUNNING_DEV`. This way we can test that scripts/flows/schedules... deployed on a previous version of Windmill won't break with the upgrade
|
||||
Some tests are skipped unless `WMILL_RUNNING_DEV == 1`, those are just regular integration tests that just needs to be run on the dev version.
|
||||
|
||||
#### Running locally
|
||||
|
||||
Running the tests locally is not easy given that we need to have a Docker image running the latest version of the code. However, the tests simply reaches Windmill API on `http://localhost:8000`, so it can easily be modified to run alongside a simple `cargo run --features enterprise`.
|
||||
|
||||
Note that Windmill Enterprise version is required, you'll need a license key for all the tests to run. It needs to be set to the environment variable: `WM_LICENSE_KEY_CI`
|
||||
|
||||
For example, to run `identity_script_test.py`, you can do the following:
|
||||
|
||||
In one terminal, run your local version of Windmill:
|
||||
|
||||
```bash
|
||||
cargo run --features enterprise
|
||||
```
|
||||
|
||||
Then in another tab, run the test:
|
||||
|
||||
```bash
|
||||
python -m unittest -v test.TestIdentityScript
|
||||
```
|
||||
|
||||
Some tests requires additional setup. This is the case of the SDK tests, which requires Windmill SDK package to be published to either private NPM registry or PyPI server. Custom setup logic can be found in (build.sh)[./build.sh].
|
||||
|
||||
#### TODOs
|
||||
|
||||
Add integration tests for:
|
||||
|
||||
- [ ] Test Python SDK
|
||||
- [ ] Test job failures as well
|
||||
- [ ] Test dedicated workers
|
||||
- [ ] Error handlers and Recovery handlers for schedule
|
||||
- [ ] Concurrency limits
|
||||
30
integration_tests/build.sh
Executable file
30
integration_tests/build.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
root_dirpath="$(cd "${script_dirpath}/.." && pwd)"
|
||||
|
||||
source ${script_dirpath}/common.sh
|
||||
|
||||
docker pull ${WM_IMAGE}:${WM_VERSION}
|
||||
docker pull ${WM_IMAGE}:${WM_VERSION_DEV}
|
||||
|
||||
previous_version=${WM_VERSION}
|
||||
custom_version="${WM_VERSION}-dev"
|
||||
|
||||
echo "Previous version was: ${previous_version}"
|
||||
${root_dirpath}/.github/change-versions.sh ${custom_version}
|
||||
|
||||
# Publishing Windmill SDK package to private NPM registry
|
||||
npm install -g npm-cli-login
|
||||
docker compose up npm_registry -d
|
||||
sleep 2
|
||||
curl -vvv -XPUT -H "Content-type: application/json" -d '{ "name": "windmill", "password": "changeme" }' 'http://localhost:4873/-/user/org.couchdb.user:windmill' | jq -r .token
|
||||
npm-cli-login -u windmill -p changeme -e admin@windmill.dev -r http://localhost:4873
|
||||
cd ${root_dirpath}/typescript-client
|
||||
${root_dirpath}/typescript-client/publish.sh '--registry http://localhost:4873'
|
||||
cd ${script_dirpath}
|
||||
docker compose down
|
||||
|
||||
# TODO: publish Python SDK to private PyPI registry
|
||||
|
||||
${root_dirpath}/.github/change-versions.sh ${previous_version}
|
||||
10
integration_tests/common.sh
Executable file
10
integration_tests/common.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
root_dirpath="$(cd "${script_dirpath}/.." && pwd)"
|
||||
|
||||
export WM_VERSION=$(cat ${root_dirpath}/version.txt)
|
||||
# WM_VERSION="1.228.1"
|
||||
export WM_VERSION_DEV="dev"
|
||||
# WM_VERSION_DEV="1.229.0"
|
||||
export WM_IMAGE="ghcr.io/windmill-labs/windmill-ee"
|
||||
108
integration_tests/docker-compose.yml
Normal file
108
integration_tests/docker-compose.yml
Normal file
@@ -0,0 +1,108 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
db:
|
||||
deploy:
|
||||
# To use an external database, set replicas to 0 and set DATABASE_URL to the external database url in the .env file
|
||||
replicas: 1
|
||||
image: postgres:14
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
expose:
|
||||
- 5432
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
POSTGRES_PASSWORD: changeme
|
||||
POSTGRES_DB: windmill
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
windmill_server:
|
||||
image: ${WM_IMAGE}:${WM_VERSION}
|
||||
labels:
|
||||
dev.windmill.role: server
|
||||
deploy:
|
||||
replicas: 1
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 8000
|
||||
ports:
|
||||
- 8000:8000
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
|
||||
- MODE=server
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:8000/api/version || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
windmill_worker:
|
||||
image: ${WM_IMAGE}:${WM_VERSION}
|
||||
labels:
|
||||
dev.windmill.role: worker
|
||||
deploy:
|
||||
replicas: 1
|
||||
resources:
|
||||
limits:
|
||||
cpus: "1"
|
||||
memory: 2048M
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
|
||||
- MODE=worker
|
||||
- WORKER_GROUP=default
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:8000/api/version || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
windmill_server:
|
||||
condition: service_healthy
|
||||
# to mount the worker folder to debug, KEEP_JOB_DIR=true and mount /tmp/windmill
|
||||
volumes:
|
||||
# mount the docker socket to allow to run docker containers from within the workers
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- worker_dependency_cache:/tmp/windmill/cache
|
||||
|
||||
npm_registry:
|
||||
image: verdaccio/verdaccio
|
||||
environment:
|
||||
- VERDACCIO_PROTOCOL=http
|
||||
- VERDACCIO_PUBLIC_URL=http://npm_registry:4873
|
||||
ports:
|
||||
- 4873:4873
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:4873/-/ping || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- ./verdaccio:/verdaccio/conf
|
||||
- npm_registry_data:/verdaccio/storage
|
||||
|
||||
pypi_server:
|
||||
image: pypiserver/pypiserver:latest
|
||||
platform: linux/x86_64
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- pypi_data:/data/packages
|
||||
command: run -P . -a . /data/packages
|
||||
|
||||
volumes:
|
||||
db_data: null
|
||||
npm_registry_data: null
|
||||
worker_dependency_cache: null
|
||||
pypi_data: null
|
||||
7
integration_tests/requirements.txt
Normal file
7
integration_tests/requirements.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
anyio==4.2.0
|
||||
certifi==2023.11.17
|
||||
h11==0.14.0
|
||||
httpcore==1.0.2
|
||||
httpx==0.26.0
|
||||
idna==3.6
|
||||
sniffio==1.3.0
|
||||
24
integration_tests/run.sh
Executable file
24
integration_tests/run.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
root_dirpath="$(cd "${script_dirpath}/.." && pwd)"
|
||||
|
||||
source ${script_dirpath}/common.sh
|
||||
|
||||
echo "Setting up python environment..."
|
||||
python --version
|
||||
python -m venv ${script_dirpath}/.venv/
|
||||
${script_dirpath}/.venv/bin/pip install -r ${script_dirpath}/requirements.txt
|
||||
|
||||
echo "Running initial round of test for version ${WM_VERSION}"
|
||||
WM_IMAGE=${WM_IMAGE} WM_VERSION=${WM_VERSION} docker compose up -d
|
||||
|
||||
mkdir -p ./logs
|
||||
echo "" > ./logs/docker-compose.log
|
||||
docker compose logs --no-color --follow &> ./logs/docker-compose.log &
|
||||
|
||||
cd ${script_dirpath} && ${script_dirpath}/.venv/bin/python -m unittest -v test
|
||||
|
||||
echo "Running second round of test for version ${WM_VERSION_DEV}"
|
||||
WM_IMAGE=${WM_IMAGE} WM_VERSION=${WM_VERSION_DEV} docker compose up -d
|
||||
WM_VERSION_DEV=${WM_VERSION}-dev WMILL_RUNNING_DEV=1 ${script_dirpath}/.venv/bin/python -m unittest -v test
|
||||
4
integration_tests/test/__init__.py
Normal file
4
integration_tests/test/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .identity_script_test import *
|
||||
from .increment_flow_test import *
|
||||
from .schedule_test import *
|
||||
from .windmill_sdk_test import *
|
||||
81
integration_tests/test/identity_script_test.py
Normal file
81
integration_tests/test/identity_script_test.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from .wmill_integration_test_utils import WindmillClient
|
||||
|
||||
PATH_TEMPLATE = "u/admin/{lang}_identity_script"
|
||||
SCRIPTS = {
|
||||
"bash": """
|
||||
x="$1"
|
||||
echo ${x}
|
||||
""",
|
||||
"bun": """
|
||||
export async function main(x: number) {
|
||||
return x;
|
||||
}
|
||||
""",
|
||||
"deno": """
|
||||
export async function main(x: number) {
|
||||
return x;
|
||||
}
|
||||
""",
|
||||
"go": """
|
||||
package inner
|
||||
func main(x int) (interface{}, error) {
|
||||
return x, nil
|
||||
}
|
||||
""",
|
||||
"python3": """
|
||||
def main(x: int):
|
||||
return x
|
||||
""",
|
||||
}
|
||||
|
||||
|
||||
class TestIdentityScript(unittest.TestCase):
|
||||
_client: WindmillClient
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
print("Running {}".format(cls.__name__))
|
||||
cls._client = WindmillClient()
|
||||
|
||||
if not os.environ.get("WMILL_RUNNING_DEV", False):
|
||||
for lang, script in SCRIPTS.items():
|
||||
cls._client.create_script(
|
||||
path=PATH_TEMPLATE.format(lang=lang),
|
||||
content=script,
|
||||
language=lang,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
if os.environ.get("WMILL_RUNNING_DEV", False):
|
||||
for lang in SCRIPTS:
|
||||
cls._client.delete_script(path=PATH_TEMPLATE.format(lang=lang))
|
||||
|
||||
def test_bash(self):
|
||||
path = PATH_TEMPLATE.format(lang="bash")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, "5") # bash only knows strings
|
||||
|
||||
def test_bun(self):
|
||||
path = PATH_TEMPLATE.format(lang="bun")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, 5)
|
||||
|
||||
def test_deno(self):
|
||||
path = PATH_TEMPLATE.format(lang="deno")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, 5)
|
||||
|
||||
def test_go(self):
|
||||
path = PATH_TEMPLATE.format(lang="go")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, 5)
|
||||
|
||||
def test_python(self):
|
||||
path = PATH_TEMPLATE.format(lang="python3")
|
||||
result = self._client.run_sync(path, {"x": 5})
|
||||
self.assertEqual(result, 5)
|
||||
156
integration_tests/test/increment_flow_test.py
Normal file
156
integration_tests/test/increment_flow_test.py
Normal file
@@ -0,0 +1,156 @@
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from .wmill_integration_test_utils import WindmillClient
|
||||
|
||||
PATH = "u/admin/increment_flow"
|
||||
FLOW_VALUE = """
|
||||
{
|
||||
"summary": "",
|
||||
"schema":
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "",
|
||||
"format": ""
|
||||
}
|
||||
},
|
||||
"required":
|
||||
[],
|
||||
"type": "object",
|
||||
"order":
|
||||
[
|
||||
"x"
|
||||
]
|
||||
},
|
||||
"value":
|
||||
{
|
||||
"modules":
|
||||
[
|
||||
{
|
||||
"id": "a",
|
||||
"value":
|
||||
{
|
||||
"tag": "",
|
||||
"lock": "{\\n \\"version\\": \\"3\\",\\n \\"remote\\": {}\\n}\\n",
|
||||
"type": "rawscript",
|
||||
"content": "export async function main(x: number) {\\n return x + 1\\n}\\n",
|
||||
"language": "deno",
|
||||
"input_transforms":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"expr": "flow_input.x + 5",
|
||||
"type": "javascript"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
"value":
|
||||
{
|
||||
"lock": "{\\n \\"dependencies\\": {}\\n}\\n//bun.lockb\\n<empty>",
|
||||
"type": "rawscript",
|
||||
"content": "export async function main(x: number) {\\n return x + 1\\n}\\n",
|
||||
"language": "bun",
|
||||
"input_transforms":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"expr": "results.a",
|
||||
"type": "javascript"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c",
|
||||
"value":
|
||||
{
|
||||
"lock": "",
|
||||
"type": "rawscript",
|
||||
"content": "def main(x: int):\\n return x + 1",
|
||||
"language": "python3",
|
||||
"input_transforms":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"expr": "results.b",
|
||||
"type": "javascript"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "d",
|
||||
"value":
|
||||
{
|
||||
"lock": "module mymod\\n\\ngo 1.21.5\\n//go.sum\\n",
|
||||
"type": "rawscript",
|
||||
"content": "package inner\\nfunc main(x int) (interface{}, error) {\\n\\treturn x + 1, nil\\n}\\n",
|
||||
"language": "go",
|
||||
"input_transforms":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"expr": "results.c",
|
||||
"type": "javascript"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e",
|
||||
"value":
|
||||
{
|
||||
"lock": "",
|
||||
"type": "rawscript",
|
||||
"content": "x=\\"$1\\"\\necho $((x+1))",
|
||||
"language": "bash",
|
||||
"input_transforms":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"expr": "`${results.d}`",
|
||||
"type": "javascript"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"extra_perms":
|
||||
{},
|
||||
"ws_error_handler_muted": false
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class TestIncrementFlow(unittest.TestCase):
|
||||
_client: WindmillClient
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
print("Running {}".format(cls.__name__))
|
||||
cls._client = WindmillClient()
|
||||
if not os.environ.get("WMILL_RUNNING_DEV", False):
|
||||
cls._client.create_flow(
|
||||
path=PATH,
|
||||
flow_value_json=FLOW_VALUE,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
if os.environ.get("WMILL_RUNNING_DEV", False):
|
||||
cls._client.delete_flow(path=PATH)
|
||||
|
||||
def test_flow(self):
|
||||
result = self._client.run_sync(PATH, {"x": 5}, type="f")
|
||||
self.assertEqual(result, "15") # bash only knows strings
|
||||
136
integration_tests/test/schedule_test.py
Normal file
136
integration_tests/test/schedule_test.py
Normal file
@@ -0,0 +1,136 @@
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from .wmill_integration_test_utils import WindmillClient
|
||||
|
||||
FLOW_SCHEDULE_PATH = "u/admin/flow_schedule"
|
||||
FLOW_PATH = "u/admin/scheduled_flow"
|
||||
FLOW_VALUE = """
|
||||
{
|
||||
"summary": "",
|
||||
"value":
|
||||
{
|
||||
"modules":
|
||||
[
|
||||
{
|
||||
"id": "a",
|
||||
"value":
|
||||
{
|
||||
"type": "rawscript",
|
||||
"content": "def main(x: int):\\n return x",
|
||||
"language": "python3",
|
||||
"input_transforms":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"type": "javascript",
|
||||
"expr": "flow_input.x"
|
||||
}
|
||||
},
|
||||
"tag": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"edited_by": "",
|
||||
"edited_at": "",
|
||||
"archived": false,
|
||||
"extra_perms":
|
||||
{},
|
||||
"schema":
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties":
|
||||
{
|
||||
"x":
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "",
|
||||
"format": ""
|
||||
}
|
||||
},
|
||||
"required":
|
||||
[],
|
||||
"type": "object",
|
||||
"order":
|
||||
[
|
||||
"x"
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
SCRIPT_SCHEDULE_PATH = "u/admin/script_schedule"
|
||||
SCRIPT_PATH = "u/admin/scheduled_script"
|
||||
SCRIPT_VALUE = """
|
||||
def main(x: int):
|
||||
return x
|
||||
"""
|
||||
|
||||
|
||||
class TestSchedule(unittest.TestCase):
|
||||
_client: WindmillClient
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
print("Running {}".format(cls.__name__))
|
||||
cls._client = WindmillClient()
|
||||
if not os.environ.get("WMILL_RUNNING_DEV", False):
|
||||
cls._client.create_flow(
|
||||
path=FLOW_PATH,
|
||||
flow_value_json=FLOW_VALUE,
|
||||
)
|
||||
cls._client.create_script(
|
||||
path=SCRIPT_PATH,
|
||||
content=SCRIPT_VALUE,
|
||||
language="python3",
|
||||
)
|
||||
cls._client.create_schedule(
|
||||
path=FLOW_SCHEDULE_PATH,
|
||||
runnable_path=FLOW_PATH,
|
||||
type="flow",
|
||||
args={"x": 5},
|
||||
)
|
||||
cls._client.create_schedule(
|
||||
path=SCRIPT_SCHEDULE_PATH,
|
||||
runnable_path=SCRIPT_PATH,
|
||||
args={"x": 8},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
if os.environ.get("WMILL_RUNNING_DEV", False):
|
||||
cls._client.delete_schedule(path=FLOW_SCHEDULE_PATH)
|
||||
cls._client.delete_schedule(path=SCRIPT_SCHEDULE_PATH)
|
||||
cls._client.delete_flow(path=FLOW_PATH)
|
||||
cls._client.delete_script(path=SCRIPT_PATH)
|
||||
|
||||
@staticmethod
|
||||
def parse_db_datetime(db_datetime: str) -> datetime.datetime:
|
||||
return datetime.datetime.fromisoformat(db_datetime.strip("Z") + "+00:00")
|
||||
|
||||
def test_script_schedule_running(self):
|
||||
# the script is scheduled to run every 5 seconds, wait for 6 before checking is has run
|
||||
time.sleep(6)
|
||||
script_runs = self._client.get_latest_job_runs(path=SCRIPT_PATH)
|
||||
if len(script_runs) == 0:
|
||||
self.fail("No script runs found")
|
||||
latest_run = script_runs[0]
|
||||
latest_run_time = TestSchedule.parse_db_datetime(latest_run["created_at"])
|
||||
time_now = datetime.datetime.now(datetime.timezone.utc)
|
||||
# check that last run is within 5 seconds of now
|
||||
self.assertTrue(time_now - latest_run_time < datetime.timedelta(seconds=5))
|
||||
|
||||
def test_flow_schedule_running(self):
|
||||
# the flow is scheduled to run every 5 seconds, wait for 6 before checking is has run
|
||||
time.sleep(6)
|
||||
flow_runs = self._client.get_latest_job_runs(path=FLOW_PATH)
|
||||
if len(flow_runs) == 0:
|
||||
self.fail("No flow runs found")
|
||||
latest_run = flow_runs[0]
|
||||
latest_run_time = TestSchedule.parse_db_datetime(latest_run["created_at"])
|
||||
time_now = datetime.datetime.now(datetime.timezone.utc)
|
||||
# check that last run is within 5 seconds of now
|
||||
self.assertTrue(time_now - latest_run_time < datetime.timedelta(seconds=5))
|
||||
127
integration_tests/test/windmill_sdk_test.py
Normal file
127
integration_tests/test/windmill_sdk_test.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from .wmill_integration_test_utils import WindmillClient
|
||||
|
||||
VARIABLE_PATH = "u/admin/test_variable"
|
||||
VARIABLE_VALUE = "Hello world!"
|
||||
|
||||
DENO_SCRIPT_PATH = "u/admin/deno_sdk_test_script"
|
||||
DENO_SCRIPT_VALUE = """
|
||||
import * as wmill from "npm:windmill-client@{version}"
|
||||
export async function main() {{
|
||||
const val = await wmill.getVariable('u/admin/test_variable')
|
||||
return val;
|
||||
}}
|
||||
"""
|
||||
|
||||
BUN_SCRIPT_PATH = "u/admin/bun_sdk_test_script"
|
||||
BUN_SCRIPT_VALUE = """
|
||||
import * as wmill from "windmill-client@{version}"
|
||||
export async function main() {{
|
||||
const val = await wmill.getVariable('u/admin/test_variable')
|
||||
return val;
|
||||
}}
|
||||
"""
|
||||
|
||||
PYTHON_SCRIPT_PATH = "u/admin/python_sdk_test_script"
|
||||
PYTHON_SCRIPT_VALUE = """
|
||||
# requirements:
|
||||
# wmill=={version}
|
||||
import wmill
|
||||
def main():
|
||||
val = wmill.get_variable("u/admin/test_variable")
|
||||
return val
|
||||
"""
|
||||
|
||||
BASH_SCRIPT_PATH = "u/admin/bash_sdk_test_script"
|
||||
BASH_SCRIPT_VALUE = """
|
||||
val=$(curl -s -H "Authorization: Bearer $WM_TOKEN" \
|
||||
"$BASE_INTERNAL_URL/api/w/$WM_WORKSPACE/variables/get_value/u/admin/test_variable" | jq -r .)
|
||||
echo "$val"
|
||||
"""
|
||||
|
||||
|
||||
class TestWindmillSdk(unittest.TestCase):
|
||||
_dev_version = os.environ.get("WM_VERSION_DEV", "0.0.0").strip()
|
||||
_running_latest = False
|
||||
_client: WindmillClient
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
print("Running {}".format(cls.__name__))
|
||||
cls._running_latest = os.environ.get("WMILL_RUNNING_DEV", "0") == "1"
|
||||
cls._client = WindmillClient()
|
||||
if cls._running_latest:
|
||||
cls._client.set_npm_config_registry("http://npm_registry:4873")
|
||||
cls._client.create_variable(
|
||||
path=VARIABLE_PATH,
|
||||
value=VARIABLE_VALUE,
|
||||
)
|
||||
cls._client.create_script(
|
||||
path=DENO_SCRIPT_PATH,
|
||||
content=DENO_SCRIPT_VALUE.format(version=cls._dev_version),
|
||||
language="deno",
|
||||
)
|
||||
# TODO: See skipped annotations below
|
||||
# cls._client.create_script(
|
||||
# path=BUN_SCRIPT_PATH,
|
||||
# content=BUN_SCRIPT_VALUE.format(version=cls._dev_version),
|
||||
# language="bun",
|
||||
# )
|
||||
# cls._client.create_script(
|
||||
# path=PYTHON_SCRIPT_PATH,
|
||||
# content=PYTHON_SCRIPT_VALUE.format(version=cls._dev_version),
|
||||
# language="python3",
|
||||
# )
|
||||
cls._client.create_script(
|
||||
path=BASH_SCRIPT_PATH,
|
||||
content=BASH_SCRIPT_VALUE,
|
||||
language="bash",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
if cls._running_latest:
|
||||
cls._client.set_npm_config_registry("")
|
||||
cls._client.delete_script(path=DENO_SCRIPT_PATH)
|
||||
# TODO: See skipped annotations below
|
||||
# cls._client.delete_script(path=BUN_SCRIPT_PATH)
|
||||
# cls._client.delete_script(path=PYTHON_SCRIPT_PATH)
|
||||
cls._client.delete_script(path=BASH_SCRIPT_PATH)
|
||||
cls._client.delete_variable(path=VARIABLE_PATH)
|
||||
cls._client.set_npm_config_registry("")
|
||||
|
||||
@unittest.skipUnless(
|
||||
os.environ.get("WMILL_RUNNING_DEV", "0") == "1", "Runs on dev version only"
|
||||
)
|
||||
def test_deno_sdk_usable(self):
|
||||
result = self._client.run_sync(DENO_SCRIPT_PATH, {})
|
||||
self.assertEqual(result, VARIABLE_VALUE)
|
||||
|
||||
@unittest.skipUnless(
|
||||
# os.environ.get("WMILL_RUNNING_DEV", "0") == "1", "Runs on dev version only"
|
||||
False,
|
||||
"TODO: Skipped for now b/c verdaccio doesn't support trailing slash at the end of URL",
|
||||
)
|
||||
def test_bun_sdk_usable(self):
|
||||
result = self._client.run_sync(BUN_SCRIPT_PATH, {})
|
||||
self.assertEqual(result, VARIABLE_VALUE)
|
||||
|
||||
@unittest.skipUnless(
|
||||
# os.environ.get("WMILL_RUNNING_DEV", "0") == "1", "Runs on dev version only"
|
||||
False,
|
||||
"TODO: Need to publish python SDK to private PiPY server",
|
||||
)
|
||||
def test_python_sdk_usable(self):
|
||||
result = self._client.run_sync(PYTHON_SCRIPT_PATH, {})
|
||||
self.assertEqual(result, VARIABLE_VALUE)
|
||||
|
||||
@unittest.skipUnless(
|
||||
os.environ.get("WMILL_RUNNING_DEV", "0") == "1", "Runs on dev version only"
|
||||
)
|
||||
def test_bash_sdk_usable(self):
|
||||
result = self._client.run_sync(BASH_SCRIPT_PATH, {})
|
||||
self.assertEqual(result, VARIABLE_VALUE)
|
||||
256
integration_tests/test/wmill_integration_test_utils.py
Normal file
256
integration_tests/test/wmill_integration_test_utils.py
Normal file
@@ -0,0 +1,256 @@
|
||||
import datetime
|
||||
import time
|
||||
import httpx
|
||||
import json
|
||||
import os
|
||||
|
||||
USERNAME = "admin"
|
||||
|
||||
|
||||
class WindmillClient:
|
||||
_url: str
|
||||
_token: str
|
||||
_workspace: str
|
||||
|
||||
_client: httpx.Client
|
||||
|
||||
def __init__(self):
|
||||
self._workspace = "integration-tests"
|
||||
self._url = "http://localhost:8000"
|
||||
self._token = self._login()
|
||||
|
||||
self._client = self._init_client()
|
||||
print("New client for Windmill version {}".format(self.get_version()))
|
||||
self._create_workspace()
|
||||
self._set_license_key()
|
||||
|
||||
def __del__(self):
|
||||
self._logout()
|
||||
self._client.close()
|
||||
|
||||
def _login(self) -> str:
|
||||
with httpx.Client(base_url=self._url) as unauth_client:
|
||||
response = unauth_client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"email": "admin@windmill.dev",
|
||||
"password": "changeme",
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def _logout(self) -> None:
|
||||
response = self._client.post(
|
||||
"/api/auth/logout",
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
|
||||
def _init_client(self):
|
||||
token = self._token
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
}
|
||||
return httpx.Client(
|
||||
base_url=self._url,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
def _set_license_key(self):
|
||||
license_key = os.environ.get("LICENSE_KEY", "").strip()
|
||||
print(
|
||||
"Setting license key to {}...{}".format(license_key[:15], license_key[-15:])
|
||||
)
|
||||
response = self._client.post(
|
||||
"/api/settings/global/license_key",
|
||||
json={
|
||||
"value": license_key,
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
|
||||
def _create_workspace(self):
|
||||
print("Creating workspace for integration tests")
|
||||
exists = self._client.post(
|
||||
"/api/workspaces/exists",
|
||||
json={
|
||||
"id": self._workspace,
|
||||
},
|
||||
)
|
||||
if exists.status_code // 100 == 2 and exists.content.decode() == "true":
|
||||
print("Workspace already exists, not creating it")
|
||||
return
|
||||
response = self._client.post(
|
||||
"/api/workspaces/create",
|
||||
json={
|
||||
"id": self._workspace,
|
||||
"name": self._workspace,
|
||||
"username": "admin",
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def set_npm_config_registry(self, registry_url: str):
|
||||
response = self._client.post(
|
||||
"/api/settings/global/npm_config_registry",
|
||||
json={
|
||||
"value": registry_url,
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
|
||||
def run_sync(self, path: str, args: dict, type: str = "p"):
|
||||
print(f"Running {path} with args {args}")
|
||||
response = self._client.post(
|
||||
f"/api/w/{self._workspace}/jobs/run_wait_result/{type}/{path}",
|
||||
json=args,
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.json()
|
||||
|
||||
def create_script(self, path: str, content: str, language: str):
|
||||
print(f"Creating script {path}")
|
||||
response = self._client.post(
|
||||
f"/api/w/{self._workspace}/scripts/create",
|
||||
json={
|
||||
"path": path,
|
||||
"content": content,
|
||||
"description": "",
|
||||
"summary": "",
|
||||
"language": language,
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
script_hash = response.content.decode()
|
||||
print(f"Script hash for path {path} is {script_hash}")
|
||||
time_now = datetime.datetime.now(datetime.timezone.utc)
|
||||
while datetime.datetime.now(
|
||||
datetime.timezone.utc
|
||||
) - time_now < datetime.timedelta(seconds=60):
|
||||
response = self._client.get(
|
||||
f"/api/w/{self._workspace}/scripts/deployment_status/h/{script_hash}"
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
elif response.json()["lock"] != None:
|
||||
# deployment successful -> return
|
||||
return
|
||||
elif response.json()["lock_error_logs"] != None:
|
||||
raise Exception(response.json()["lock_error_logs"])
|
||||
print(f"Waiting for script {path} with hash {script_hash} to be deployed")
|
||||
time.sleep(1)
|
||||
raise Exception(f"Script deployment failed for {path}")
|
||||
|
||||
def delete_script(self, path: str):
|
||||
print(f"Deleting script {path}")
|
||||
response = self._client.post(
|
||||
f"/api/w/{self._workspace}/scripts/delete/p/{path}",
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def create_flow(self, path: str, flow_value_json: str):
|
||||
print(f"Creating flow {path}")
|
||||
parsed_flow = json.loads(flow_value_json)
|
||||
if "path" not in parsed_flow:
|
||||
parsed_flow["path"] = path
|
||||
response = self._client.post(
|
||||
f"/api/w/{self._workspace}/flows/create",
|
||||
json=parsed_flow,
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def delete_flow(self, path: str):
|
||||
print(f"Deleting flow {path}")
|
||||
response = self._client.delete(
|
||||
f"/api/w/{self._workspace}/flows/delete/{path}",
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def create_schedule(
|
||||
self,
|
||||
path: str,
|
||||
runnable_path: str,
|
||||
type: str = "script",
|
||||
schedule: str = "*/5 * * * * *",
|
||||
args: dict = {},
|
||||
):
|
||||
print(f"Creating schedule {path}")
|
||||
response = self._client.post(
|
||||
f"/api/w/{self._workspace}/schedules/create",
|
||||
json={
|
||||
"path": path,
|
||||
"schedule": schedule,
|
||||
"timezone": "Europe/Paris",
|
||||
"script_path": runnable_path,
|
||||
"is_flow": type == "flow",
|
||||
"args": args,
|
||||
"enabled": True,
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def delete_schedule(self, path: str):
|
||||
print(f"Deleting schedule {path}")
|
||||
response = self._client.delete(
|
||||
f"/api/w/{self._workspace}/schedules/delete/{path}",
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def create_variable(
|
||||
self,
|
||||
path: str,
|
||||
value: str,
|
||||
):
|
||||
print(f"Creating variable {path} with value '{value}'")
|
||||
response = self._client.post(
|
||||
f"/api/w/{self._workspace}/variables/create",
|
||||
json={
|
||||
"path": path,
|
||||
"value": value,
|
||||
"description": "",
|
||||
"is_secret": False,
|
||||
},
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def delete_variable(self, path: str):
|
||||
print(f"Deleting variable {path}")
|
||||
response = self._client.delete(
|
||||
f"/api/w/{self._workspace}/variables/delete/{path}",
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.content.decode()
|
||||
|
||||
def get_latest_job_runs(self, path: str):
|
||||
response = self._client.get(
|
||||
f"/api/w/{self._workspace}/jobs/list?script_path_exact={path}"
|
||||
)
|
||||
if response.status_code // 100 != 2:
|
||||
raise Exception(response.content.decode())
|
||||
return response.json()
|
||||
|
||||
def get_version(self):
|
||||
response = self._client.get("/api/version")
|
||||
return response.content.decode()
|
||||
17
integration_tests/verdaccio/config.yaml
Normal file
17
integration_tests/verdaccio/config.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
storage: /verdaccio/storage/data
|
||||
plugins: /verdaccio/plugins
|
||||
auth:
|
||||
htpasswd:
|
||||
file: /verdaccio/storage/htpasswd
|
||||
uplinks:
|
||||
npmjs:
|
||||
url: https://registry.npmjs.org/
|
||||
packages:
|
||||
"**":
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
proxy: npmjs
|
||||
listen:
|
||||
- http://localhost:4873
|
||||
- http://npm_registry:4873
|
||||
log: { type: stdout, format: pretty, level: http }
|
||||
@@ -2,6 +2,8 @@
|
||||
set -eou pipefail
|
||||
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
args=${1:-}
|
||||
|
||||
rm -rf "${script_dirpath}/dist"
|
||||
|
||||
${script_dirpath}/build.sh
|
||||
@@ -10,4 +12,4 @@ rm "${script_dirpath}/s3Types.ts"
|
||||
tsc
|
||||
cp "${script_dirpath}/src/client.ts" ${script_dirpath}
|
||||
cp "${script_dirpath}/src/s3Types.ts" ${script_dirpath}
|
||||
npm publish
|
||||
npm publish ${args}
|
||||
|
||||
Reference in New Issue
Block a user