diff --git a/.github/workflows/serve-shinyproxy.yml b/.github/workflows/serve-shinyproxy.yml new file mode 100644 index 0000000..f7c466c --- /dev/null +++ b/.github/workflows/serve-shinyproxy.yml @@ -0,0 +1,78 @@ +name: Serve-Shinyproxy workflow + +on: + push: + paths: + - "serve-shinyproxy/**" + +jobs: + build_and_test: + runs-on: ubuntu-latest + + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@main + + - name: 'Build test image' + run: | + docker build -t ghcr.io/scilifelabdatacentre/serve-shinyproxy:latest ./serve-shinyproxy + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.7.1 + with: + image-ref: 'ghcr.io/scilifelabdatacentre/serve-shinyproxy:latest' + format: 'table' + severity: 'CRITICAL,HIGH' + timeout: '30m0s' + exit-code: '0' + + - name: 'Run tests' + env: + IMAGE_NAME: ghcr.io/scilifelabdatacentre/serve-shinyproxy:latest + run: | + pip install -r ./serve-shinyproxy/tests/requirements.txt + python3 -m pytest ./serve-shinyproxy + + push: + if: | + github.ref == 'refs/heads/main' && + github.repository == 'scilifelabdatacentre/serve-images' + needs: build_and_test + runs-on: ubuntu-latest + concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + permissions: + contents: read + packages: write + + steps: + - name: 'Checkout github action' + uses: actions/checkout@main + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ghcr.io/scilifelabdatacentre/serve-shinyproxy + tags: | + type=raw,value={{date 'YYMMDD-HHmm' tz='Europe/Stockholm'}} + + - name: 'Login to GHCR' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{github.actor}} + password: ${{secrets.GITHUB_TOKEN}} + + - name: Publish image to GHCR + uses: docker/build-push-action@v3 + with: + file: ./serve-shinyproxy/Dockerfile + context: ./serve-shinyproxy + push: true + build-args: version=${{ github.ref_name }} + tags: | + ${{ steps.meta.outputs.tags }} + ghcr.io/scilifelabdatacentre/serve-shinyproxy:latest + labels: ${{ steps.meta.outputs.labels }} diff --git a/dev_scripts/run_shinyproxy.sh b/dev_scripts/run_shinyproxy.sh new file mode 100755 index 0000000..e331dc7 --- /dev/null +++ b/dev_scripts/run_shinyproxy.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -o errexit + +docker build -t shinyproxy-dev-img ../serve-shinyproxy +python3 -m venv venv +source ./venv/bin/activate +python3 -m pip install --upgrade pip +pip install -r ../serve-shinyproxy/tests/requirements.txt +export IMAGE_NAME=shinyproxy-dev-img +python3 -m pytest ../serve-shinyproxy/ diff --git a/serve-shinyproxy/Dockerfile b/serve-shinyproxy/Dockerfile new file mode 100644 index 0000000..b09bfee --- /dev/null +++ b/serve-shinyproxy/Dockerfile @@ -0,0 +1,9 @@ +FROM openanalytics/shinyproxy:3.0.2 + +ENV SHINY_USER shinyproxy +RUN chown $SHINY_USER:$SHINY_USER /opt/shinyproxy/ + +WORKDIR /opt/shinyproxy +USER $SHINY_USER + +CMD ["java", "-noverify", "-jar", "/opt/shinyproxy/shinyproxy.jar", "--spring.jmx.enabled=false", "--spring.config.location=/opt/shinyproxy/application.yml"] diff --git a/serve-shinyproxy/tests/requirements.txt b/serve-shinyproxy/tests/requirements.txt new file mode 100644 index 0000000..eb3fedf --- /dev/null +++ b/serve-shinyproxy/tests/requirements.txt @@ -0,0 +1,2 @@ +docker +pytest \ No newline at end of file diff --git a/serve-shinyproxy/tests/test_api.py b/serve-shinyproxy/tests/test_api.py new file mode 100644 index 0000000..655d794 --- /dev/null +++ b/serve-shinyproxy/tests/test_api.py @@ -0,0 +1,61 @@ +import os +import requests +import docker +import time + +PORT = 8080 # Shinyproxy port +TIMEOUT_CALL = 5 # the timeout in seconds of the client request call + +client = docker.from_env() +container = client.containers.run( + image=os.environ["IMAGE_NAME"], + ports={f"{PORT}/tcp": PORT}, + detach=True, +) +time.sleep(10) # Update this somehow. +container.reload() + + +def test_container_status(): + """Test that the Shinyproxy container is running.""" + assert container.status == "running" + + +def test_container_ports(): + """Test of the expected container port.""" + assert len(container.ports) == 1, "There should be 1 port" + assert container.ports[f"{PORT}/tcp"][0]["HostPort"] == str(PORT) + + +def test_login(): + url = _get_url(container) + "/login" + max_attempts = 50 + + for attempt in range(1, max_attempts + 1): + try: + print("URL is: ", url) + response = requests.get(url, timeout=TIMEOUT_CALL) + if response.status_code == 200: + print("Shinyproxy is up and running!") + assert True + break + except: + pass + + if attempt == max_attempts: + RuntimeError( + f"Shinyproxy did not become ready after {max_attempts} attempts" + ) + assert False + + print( + f"Attempt {attempt} failed, waiting for 10 seconds before trying again..." + ) + time.sleep(10) + + +def _get_url(container): + """Gets the URL of the container.""" + ip = container.attrs["NetworkSettings"]["Networks"]["bridge"]["IPAddress"] + url = "http://{}:{}".format(ip, PORT) + return url