diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..48b03a5c --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,132 @@ +# This is a GitHub workflow defining a set of jobs with a set of steps. +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +# +name: Tests + +on: + pull_request: + paths-ignore: + - "docs/**" + - "**.md" + - ".github/workflows/*.yaml" + - "!.github/workflows/test.yaml" + push: + paths-ignore: + - "docs/**" + - "**.md" + - ".github/workflows/*.yaml" + - "!.github/workflows/test.yaml" + branches-ignore: + - "dependabot/**" + - "pre-commit-ci-update-config" + tags: ["**"] + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-22.04 + timeout-minutes: 10 + + strategy: + fail-fast: false + matrix: + include: + - python-version: "3.7" + pip-install-spec: "jupyterhub==1.0.0 sqlalchemy==1.*" + - python-version: "3.8" + pip-install-spec: "jupyterhub==2.* sqlalchemy==1.*" + - python-version: "3.9" + pip-install-spec: "jupyterhub==3.*" + - python-version: "3.11" + pip-install-spec: "jupyterhub==4.*" + - python-version: "3.11" + pip-install-spec: "jupyterhub==4.*" + test-variation: internal-ssl + - python-version: "3.11" + pip-install-spec: "jupyterhub==4.*" + test-variation: podman + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - uses: actions/setup-node@v3 + with: + node-version: "18" + + - name: setup docker swarm + run: docker swarm init + + - name: Install Python dependencies + run: | + pip install ${{ matrix.pip-install-spec }} + pip install -e "." -r dev-requirements.txt + + - name: List Python dependencies + run: | + pip freeze + + - name: Install Node dependencies + run: | + npm install -g configurable-http-proxy + + - name: Run tests + if: matrix.test-variation == '' + run: | + pytest tests --cov=dockerspawner + + - name: Run examples/internal-ssl tests + if: matrix.test-variation == 'internal-ssl' + # FIXME: --cov=dockerspawner is omitted as the tested code lives inside + # the built dockerspawner image, so --cov=dockerspawner + # referencing the local source code doesn't get us test coverage. + # + run: | + pytest examples/internal-ssl --capture=no + + - name: Run podman tests + continue-on-error: true + if: matrix.test-variation == 'podman' + # Podman's system service is started as a user managed process + # (user-mode / rootless), and the docker-api provided by podman is used + # by setting DOCKER_HOST to the podman provided docker-api. + # + # ref: https://docs.podman.io/en/latest/markdown/podman-system-service.1.html + # ref: https://docker-py.readthedocs.io/en/stable/client.html#envvar-DOCKER_HOST + # + run: | + # Default is unix://$XDG_RUNTIME_DIR/podman/podman.sock but XDG_RUNTIME_DIR may not be set + export DOCKER_HOST=unix://$HOME/podman.sock + + podman system service --time=0 $DOCKER_HOST & + + for n in $(seq 1 10); do + if ! docker version &>/dev/null; then + echo "podman system service - starting..." + sleep 1 + else + echo "podman system service - started!" + echo "" + break + fi + done + + # FIXME: we run into an error described in + # https://github.com/containers/podman/blob/main/troubleshooting.md#26-running-containers-with-resource-limits-fails-with-a-permissions-error + # + # This is a check for permissions of relevance, where we + # conclude we get "memory pids" on ubuntu-22.04. This confirms + # that we need additional permissions for the podman system + # service we have started as the current user, lacking such + # permissions. + # + # If this is resolved, also remove the continue-on-error + # section above. + # + cat "/sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers" + + pytest tests/test_dockerspawner.py --cov=dockerspawner + + # GitHub action reference: https://github.com/codecov/codecov-action + - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 29994b25..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,117 +0,0 @@ -# This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions -# -name: Tests - -on: - pull_request: - push: - workflow_dispatch: - -jobs: - # Run tests - test: - runs-on: ubuntu-20.04 - timeout-minutes: 10 - - strategy: - fail-fast: false - matrix: - include: - - python: 3.8 - test: internal-ssl - - python: 3.7 - jupyterhub: 1.5 - - python: 3.8 - jupyterhub: 2.0 - - python: 3.9 - jupyterhub: 2.2 - - python: "3.10" - jupyterhub: "2.1" - test: podman - - python: 3.9 - jupyterhub: 3.1.1 - - python: 3.11 - jupyterhub: 3.1.1 - - steps: - - uses: actions/checkout@v2 - - - name: setup docker swarm - run: docker swarm init - - - name: Install Python ${{ matrix.python }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python }} - - # preserve pip cache to speed up installation - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - # Look to see if there is a cache hit for the corresponding requirements file - key: ${{ runner.os }}-pip-${{ hashFiles('*requirements.txt') }} - - - name: Install Python dependencies - run: | - pip install --upgrade pip - pip install --upgrade --pre -r dev-requirements.txt - if [[ ! -z "${{ matrix.jupyterhub }}" ]]; then - pip install jupyterhub==${{ matrix.jupyterhub }}.* - fi - pip install -e . - - pip freeze - - - name: Install Node - uses: actions/setup-node@v1 - with: - node-version: "14" - - - name: Install Node dependencies - run: | - npm install -g configurable-http-proxy - - - name: Pull images - if: matrix.test != 'podman' - run: | - for v in 1.0 1.1 1.2 1.3; do - docker pull jupyterhub/singleuser:$v - # preserve the layers with a different tag - docker tag jupyterhub/singleuser:$v jupyterhub/singleuser:${v}-cache - # untag so that pull actions are still required - docker rmi jupyterhub/singleuser:$v - done - - - name: Run tests - if: matrix.test != 'internal-ssl' && matrix.test != 'podman' - run: | - py.test --cov dockerspawner tests -v - - - name: Run internal-ssl tests - if: matrix.test == 'internal-ssl' - run: | - cd examples/internal-ssl - pytest -vsx - - - name: Run user-mode podman tests - if: matrix.test == 'podman' - run: | - sudo systemctl stop docker - # Default is unix://$XDG_RUNTIME_DIR/podman/podman.sock but XDG_RUNTIME_DIR may not be set - export DOCKER_HOST=unix://$HOME/podman.sock - podman system service --time=0 $DOCKER_HOST & - for n in $(seq 1 10); do - if ! docker version; then - sleep 2 - else - break - fi - done - docker ps - py.test --cov dockerspawner tests/test_dockerspawner.py -v - - - name: Submit codecov report - run: | - codecov diff --git a/dev-requirements.txt b/dev-requirements.txt index 079eb6ca..337b1d3d 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,5 @@ -codecov netifaces notebook<7 -pyflakes pytest>=3.6 pytest-asyncio pytest-cov diff --git a/pyproject.toml b/pyproject.toml index 54de4d17..75ebcf79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,6 @@ profile = "black" [tool.black] skip-string-normalization = true target_version = [ - "py36", "py37", "py38", "py39", @@ -27,7 +26,15 @@ target_version = [ # ref: https://docs.pytest.org/en/stable/ # [tool.pytest.ini_options] -addopts = "--verbose --color=yes --durations=10" +addopts = "--verbose --color=yes --durations=10 --maxfail=1" asyncio_mode = "auto" -# Ignore thousands of tests in dependencies installed in a virtual environment -norecursedirs = "lib lib64" +testpaths = ["tests"] +# These markers are registered to avoid warnings triggered by importing from +# jupyterhub.tests.test_api. +markers = [ + "role", + "user", + "slow", + "group", + "services", +] diff --git a/tests/conftest.py b/tests/conftest.py index c792bc88..d846be1f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -118,8 +118,9 @@ def docker(): c.remove() try: services = d.services.list() - except APIError: + except (APIError, TypeError): # e.g. services not available + # podman gives TypeError return else: for s in services: