From 993fe4a31e41b5f0e4610c4d0a99baa43173c8c1 Mon Sep 17 00:00:00 2001 From: GeorgianaElena Date: Fri, 19 Mar 2021 14:47:33 +0200 Subject: [PATCH 1/5] Move integration basic tests to GHA --- .github/integration-test.py | 189 ++++++++++++++++++++++++ .github/workflows/integration-test.yaml | 29 ++++ 2 files changed, 218 insertions(+) create mode 100755 .github/integration-test.py create mode 100644 .github/workflows/integration-test.yaml diff --git a/.github/integration-test.py b/.github/integration-test.py new file mode 100755 index 0000000..3fdf06f --- /dev/null +++ b/.github/integration-test.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +import argparse +import subprocess +import os + + +def build_systemd_image(image_name, source_path): + """ + Build docker image with systemd at source_path. + + Built image is tagged with image_name + """ + subprocess.check_call([ + 'docker', 'build', '-t', image_name, source_path + ]) + + +def run_systemd_image(image_name, container_name, bootstrap_pip_spec): + """ + Run docker image with systemd + + Image named image_name should be built with build_systemd_image. + + Container named container_name will be started. + """ + cmd = [ + 'docker', 'run', + '--privileged', + '--mount', 'type=bind,source=/sys/fs/cgroup,target=/sys/fs/cgroup', + '--detach', + '--name', container_name, + # This is the minimum VM size we support. JupyterLab extensions seem + # to need at least this much RAM to build. Boo? + # If we change this, need to change all other references to this number. + '--memory', '1.5G', + ] + + if bootstrap_pip_spec: + cmd.append('-e') + cmd.append(f'TLJH_BOOTSTRAP_PIP_SPEC={bootstrap_pip_spec}') + + cmd.append(image_name) + + subprocess.check_call(cmd) + + +def stop_container(container_name): + """ + Stop & remove docker container if it exists. + """ + try: + subprocess.check_output([ + 'docker', 'inspect', container_name + ], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + # No such container exists, nothing to do + return + subprocess.check_call([ + 'docker', 'rm', '-f', container_name + ]) + + +def run_container_command(container_name, cmd): + """ + Run cmd in a running container with a bash shell + """ + proc = subprocess.run([ + 'docker', 'exec', + '-t', container_name, + '/bin/bash', '-c', cmd + ], check=True) + + +def copy_to_container(container_name, src_path, dest_path): + """ + Copy files from src_path to dest_path inside container_name + """ + subprocess.check_call([ + 'docker', 'cp', + src_path, f'{container_name}:{dest_path}' + ]) + + +def run_test(image_name, test_name, bootstrap_pip_spec, test_files, upgrade, installer_args): + """ + Wrapper that sets up tljh with installer_args & runs test_name + """ + stop_container(test_name) + run_systemd_image(image_name, test_name, bootstrap_pip_spec) + + source_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir) + ) + + copy_to_container(test_name, os.path.join(source_path, 'bootstrap/.'), '/srv/src') + copy_to_container(test_name, os.path.join(source_path, 'integration-tests/'), '/srv/src') + + + # Install TLJH master first to test upgrades + if upgrade: + run_container_command( + test_name, + f'curl -L https://tljh.jupyter.org/bootstrap.py | python3 -' + ) + + run_container_command( + test_name, + f'python3 /srv/src/bootstrap.py {installer_args}' + ) + + # Install pkgs from requirements in hub's pip, where + # the bootstrap script installed the others + run_container_command( + test_name, + '/opt/tljh/hub/bin/python3 -m pip install -r /srv/src/integration-tests/requirements.txt' + ) + run_container_command( + test_name, + '/opt/tljh/hub/bin/python3 -m pytest -v {}'.format( + ' '.join([os.path.join('/srv/src/integration-tests/', f) for f in test_files]) + ) + ) + + +def show_logs(container_name): + """ + Print logs from inside container to stdout + """ + run_container_command( + container_name, + 'journalctl --no-pager' + ) + run_container_command( + container_name, + 'systemctl --no-pager status jupyterhub traefik' + ) + +def main(): + argparser = argparse.ArgumentParser() + subparsers = argparser.add_subparsers(dest='action') + + subparsers.add_parser('build-image') + subparsers.add_parser('stop-container').add_argument( + 'container_name' + ) + subparsers.add_parser('start-container').add_argument( + 'container_name' + ) + run_parser = subparsers.add_parser('run') + run_parser.add_argument('container_name') + run_parser.add_argument('command') + + copy_parser = subparsers.add_parser('copy') + copy_parser.add_argument('container_name') + copy_parser.add_argument('src') + copy_parser.add_argument('dest') + + run_test_parser = subparsers.add_parser('run-test') + run_test_parser.add_argument('--installer-args', default='') + run_test_parser.add_argument('--upgrade', action='store_true') + run_test_parser.add_argument('--bootstrap-pip-spec', nargs='?', default="", type=str) + run_test_parser.add_argument('test_name') + run_test_parser.add_argument('test_files', nargs='+') + + show_logs_parser = subparsers.add_parser('show-logs') + show_logs_parser.add_argument('container_name') + + args = argparser.parse_args() + + image_name = 'tljh-systemd' + + if args.action == 'run-test': + run_test(image_name, args.test_name, args.bootstrap_pip_spec, args.test_files, args.upgrade, args.installer_args) + elif args.action == 'show-logs': + show_logs(args.container_name) + elif args.action == 'run': + run_container_command(args.container_name, args.command) + elif args.action == 'copy': + copy_to_container(args.container_name, args.src, args.dest) + elif args.action == 'start-container': + run_systemd_image(image_name, args.container_name, args.bootstrap_pip_spec) + elif args.action == 'stop-container': + stop_container(args.container_name) + elif args.action == 'build-image': + build_systemd_image(image_name, 'integration-tests') + + +if __name__ == '__main__': + main() diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml new file mode 100644 index 0000000..88a23f8 --- /dev/null +++ b/.github/workflows/integration-test.yaml @@ -0,0 +1,29 @@ +on: + pull_request: + push: + workflow_dispatch: + +jobs: + integration-test: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.6 + - name: Build systemd image + run: | + .github/integration-test.py build-image + - name: Run bootstrap checks + run: | + python3 -m pip install pytest + pytest integration-tests/test_bootstrap.py -s + - name: Run basic tests + run: | + BOOTSTRAP_PIP_SPEC=git+https://github.com/$GITHUB_ACTOR/the-littlest-jupyterhub.git@$GITHUB_SHA + echo $BOOTSTRAP_PIP_SPEC + + .github/integration-test.py run-test \ + --bootstrap-pip-spec $BOOTSTRAP_PIP_SPEC \ + basic-tests test_hub.py test_proxy.py \ + test_install.py test_extensions.py From f2f4a04ca77395b8a0a865bcbd86e038c00a44b8 Mon Sep 17 00:00:00 2001 From: GeorgianaElena Date: Wed, 24 Mar 2021 10:55:36 +0200 Subject: [PATCH 2/5] Move admin and plugin tests to GHA --- .github/workflows/integration-test.yaml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml index 88a23f8..0261967 100644 --- a/.github/workflows/integration-test.yaml +++ b/.github/workflows/integration-test.yaml @@ -11,6 +11,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.6 + - name: Set BOOTSTRAP_PIP_SPEC value + run: | + echo "BOOTSTRAP_PIP_SPEC=git+https://github.com/$GITHUB_ACTOR/the-littlest-jupyterhub.git@$GITHUB_SHA" >> $GITHUB_ENV - name: Build systemd image run: | .github/integration-test.py build-image @@ -20,10 +23,19 @@ jobs: pytest integration-tests/test_bootstrap.py -s - name: Run basic tests run: | - BOOTSTRAP_PIP_SPEC=git+https://github.com/$GITHUB_ACTOR/the-littlest-jupyterhub.git@$GITHUB_SHA - echo $BOOTSTRAP_PIP_SPEC - .github/integration-test.py run-test \ - --bootstrap-pip-spec $BOOTSTRAP_PIP_SPEC \ - basic-tests test_hub.py test_proxy.py \ - test_install.py test_extensions.py + --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ + basic-tests test_hub.py test_proxy.py \ + test_install.py test_extensions.py + - name: Run admin tests + run: | + .github/integration-test.py run-test \ + --installer-args "--admin admin:admin" \ + --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ + basic-tests test_admin_installer.py \ + - name: Run plugin tests + run: | + .github/integration-test.py run-test \ + --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ + --installer-args "--plugin /srv/src/integration-tests/plugins/simplest" \ + plugins test_simplest_plugin.py \ From a38b27ebebf855359da16f103e2931e308f3bdb6 Mon Sep 17 00:00:00 2001 From: GeorgianaElena Date: Wed, 24 Mar 2021 12:13:29 +0200 Subject: [PATCH 3/5] Remove integration tests from CircleCI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 48eafc8..dc7841f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -82,6 +82,7 @@ commands: jobs: +<<<<<<< HEAD integration-test: docker: - image: docker:18.05.0-ce-git @@ -145,5 +146,4 @@ workflows: version: 2 all-tests: jobs: - - integration-test - upgrade-test From 2ff60d2ce6295952a63beb03f1b5fc8246cb17a0 Mon Sep 17 00:00:00 2001 From: GeorgianaElena Date: Mon, 29 Mar 2021 14:05:03 +0300 Subject: [PATCH 4/5] Add upgrade test --- .circleci/config.yml | 67 ---------- .circleci/integration-test.py | 189 ---------------------------- .github/workflows/upgrade-test.yaml | 41 ++++++ 3 files changed, 41 insertions(+), 256 deletions(-) delete mode 100755 .circleci/integration-test.py create mode 100644 .github/workflows/upgrade-test.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index dc7841f..b176603 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -80,70 +80,3 @@ commands: command: | py.test integration-tests/test_bootstrap.py -s - -jobs: -<<<<<<< HEAD - integration-test: - docker: - - image: docker:18.05.0-ce-git - - steps: - - run: - name: setup python3 - command: | - apk add --no-cache python3 pytest - - - checkout - - - setup_remote_docker - - - build_systemd_image - - - bootstrap_checks - - - basic_tests - - - admin_tests - - - plugin_tests - - upgrade-test: - docker: - - image: docker:18.05.0-ce-git - - steps: - - run: - name: Check upgrade testing - command: | - if [ "$CIRCLE_BRANCH" == "master" ]; then - echo "On master, no upgrade to test..." - circleci-agent step halt - else - echo "PR detected, testing upgrade..." - fi - - - run: - name: setup python3 - command: | - apk add --no-cache python3 pytest - - - checkout - - - setup_remote_docker - - - build_systemd_image - - - basic_tests: - upgrade: "--upgrade" - - - admin_tests: - upgrade: "--upgrade" - - - plugin_tests: - upgrade: "--upgrade" - -workflows: - version: 2 - all-tests: - jobs: - - upgrade-test diff --git a/.circleci/integration-test.py b/.circleci/integration-test.py deleted file mode 100755 index 8ed8330..0000000 --- a/.circleci/integration-test.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import subprocess -import os - - -def build_systemd_image(image_name, source_path): - """ - Build docker image with systemd at source_path. - - Built image is tagged with image_name - """ - subprocess.check_call([ - 'docker', 'build', '-t', image_name, source_path - ]) - - -def run_systemd_image(image_name, container_name, bootstrap_pip_spec): - """ - Run docker image with systemd - - Image named image_name should be built with build_systemd_image. - - Container named container_name will be started. - """ - cmd = [ - 'docker', 'run', - '--privileged', - '--mount', 'type=bind,source=/sys/fs/cgroup,target=/sys/fs/cgroup', - '--detach', - '--name', container_name, - # This is the minimum VM size we support. JupyterLab extensions seem - # to need at least this much RAM to build. Boo? - # If we change this, need to change all other references to this number. - '--memory', '1.5G', - ] - - if bootstrap_pip_spec: - cmd.append('-e') - cmd.append(f'TLJH_BOOTSTRAP_PIP_SPEC={bootstrap_pip_spec}') - - cmd.append(image_name) - - subprocess.check_call(cmd) - - -def stop_container(container_name): - """ - Stop & remove docker container if it exists. - """ - try: - subprocess.check_output([ - 'docker', 'inspect', container_name - ], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - # No such container exists, nothing to do - return - subprocess.check_call([ - 'docker', 'rm', '-f', container_name - ]) - - -def run_container_command(container_name, cmd): - """ - Run cmd in a running container with a bash shell - """ - proc = subprocess.run([ - 'docker', 'exec', - '-it', container_name, - '/bin/bash', '-c', cmd - ], check=True) - - -def copy_to_container(container_name, src_path, dest_path): - """ - Copy files from src_path to dest_path inside container_name - """ - subprocess.check_call([ - 'docker', 'cp', - src_path, f'{container_name}:{dest_path}' - ]) - - -def run_test(image_name, test_name, bootstrap_pip_spec, test_files, upgrade, installer_args): - """ - Wrapper that sets up tljh with installer_args & runs test_name - """ - stop_container(test_name) - run_systemd_image(image_name, test_name, bootstrap_pip_spec) - - source_path = os.path.abspath( - os.path.join(os.path.dirname(__file__), os.pardir) - ) - - copy_to_container(test_name, os.path.join(source_path, 'bootstrap/.'), '/srv/src') - copy_to_container(test_name, os.path.join(source_path, 'integration-tests/'), '/srv/src') - - - # Install TLJH master first to test upgrades - if upgrade: - run_container_command( - test_name, - f'curl -L https://tljh.jupyter.org/bootstrap.py | python3 -' - ) - - run_container_command( - test_name, - f'python3 /srv/src/bootstrap.py {installer_args}' - ) - - # Install pkgs from requirements in hub's pip, where - # the bootstrap script installed the others - run_container_command( - test_name, - '/opt/tljh/hub/bin/python3 -m pip install -r /srv/src/integration-tests/requirements.txt' - ) - run_container_command( - test_name, - '/opt/tljh/hub/bin/python3 -m pytest -v {}'.format( - ' '.join([os.path.join('/srv/src/integration-tests/', f) for f in test_files]) - ) - ) - - -def show_logs(container_name): - """ - Print logs from inside container to stdout - """ - run_container_command( - container_name, - 'journalctl --no-pager' - ) - run_container_command( - container_name, - 'systemctl --no-pager status jupyterhub traefik' - ) - -def main(): - argparser = argparse.ArgumentParser() - subparsers = argparser.add_subparsers(dest='action') - - subparsers.add_parser('build-image') - subparsers.add_parser('stop-container').add_argument( - 'container_name' - ) - subparsers.add_parser('start-container').add_argument( - 'container_name' - ) - run_parser = subparsers.add_parser('run') - run_parser.add_argument('container_name') - run_parser.add_argument('command') - - copy_parser = subparsers.add_parser('copy') - copy_parser.add_argument('container_name') - copy_parser.add_argument('src') - copy_parser.add_argument('dest') - - run_test_parser = subparsers.add_parser('run-test') - run_test_parser.add_argument('--installer-args', default='') - run_test_parser.add_argument('--upgrade', action='store_true') - run_test_parser.add_argument('--bootstrap-pip-spec', nargs='?', default="", type=str) - run_test_parser.add_argument('test_name') - run_test_parser.add_argument('test_files', nargs='+') - - show_logs_parser = subparsers.add_parser('show-logs') - show_logs_parser.add_argument('container_name') - - args = argparser.parse_args() - - image_name = 'tljh-systemd' - - if args.action == 'run-test': - run_test(image_name, args.test_name, args.bootstrap_pip_spec, args.test_files, args.upgrade, args.installer_args) - elif args.action == 'show-logs': - show_logs(args.container_name) - elif args.action == 'run': - run_container_command(args.container_name, args.command) - elif args.action == 'copy': - copy_to_container(args.container_name, args.src, args.dest) - elif args.action == 'start-container': - run_systemd_image(image_name, args.container_name, args.bootstrap_pip_spec) - elif args.action == 'stop-container': - stop_container(args.container_name) - elif args.action == 'build-image': - build_systemd_image(image_name, 'integration-tests') - - -if __name__ == '__main__': - main() diff --git a/.github/workflows/upgrade-test.yaml b/.github/workflows/upgrade-test.yaml new file mode 100644 index 0000000..dba6ef5 --- /dev/null +++ b/.github/workflows/upgrade-test.yaml @@ -0,0 +1,41 @@ +on: + pull_request: + push: + workflow_dispatch: + +jobs: + upgrade-test: + if: ${{ github.ref != 'refs/heads/master' }} + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.6 + - name: Set BOOTSTRAP_PIP_SPEC value + run: | + echo "BOOTSTRAP_PIP_SPEC=git+https://github.com/$GITHUB_ACTOR/the-littlest-jupyterhub.git@$GITHUB_SHA" >> $GITHUB_ENV + - name: Build systemd image + run: | + .github/integration-test.py build-image + - name: Run basic tests + run: | + .github/integration-test.py run-test \ + --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ + basic-tests test_hub.py test_proxy.py \ + test_install.py test_extensions.py \ + --upgrade + - name: Run admin tests + run: | + .github/integration-test.py run-test \ + --installer-args "--admin admin:admin" \ + --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ + basic-tests test_admin_installer.py \ + --upgrade + - name: Run plugin tests + run: | + .github/integration-test.py run-test \ + --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ + --installer-args "--plugin /srv/src/integration-tests/plugins/simplest" \ + plugins test_simplest_plugin.py \ + --upgrade From df110f48e0b7e24921f4c4b08b04299f1c7c4996 Mon Sep 17 00:00:00 2001 From: GeorgianaElena Date: Tue, 30 Mar 2021 11:02:47 +0300 Subject: [PATCH 5/5] Remove all CircleCI config --- .circleci/config.yml | 82 -------------------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b176603..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,82 +0,0 @@ -version: 2.1 - -commands: - build_systemd_image: - steps: - - run: - name: build systemd image - command: | - .circleci/integration-test.py build-image - - basic_tests: - parameters: - # Whether or not we should run update tests - upgrade: - type: string - default: "" - steps: - - run: - name: Run basic tests - command: | - if [ $CIRCLE_PR_USERNAME ]; then - BOOTSTRAP_PIP_SPEC=git+https://github.com/$CIRCLE_PR_USERNAME/the-littlest-jupyterhub.git@$CIRCLE_SHA1 - else - BOOTSTRAP_PIP_SPEC=git+https://github.com/$CIRCLE_PROJECT_USERNAME/the-littlest-jupyterhub.git@$CIRCLE_SHA1 - fi - - .circleci/integration-test.py run-test \ - --bootstrap-pip-spec "$BOOTSTRAP_PIP_SPEC" \ - basic-tests test_hub.py test_proxy.py \ - test_install.py test_extensions.py \ - << parameters.upgrade >> - - admin_tests: - parameters: - upgrade: - type: string - default: "" - steps: - - run: - name: Run admin tests - command: | - if [ $CIRCLE_PR_USERNAME ]; then - BOOTSTRAP_PIP_SPEC=git+https://github.com/$CIRCLE_PR_USERNAME/the-littlest-jupyterhub.git@$CIRCLE_SHA1 - else - BOOTSTRAP_PIP_SPEC=git+https://github.com/$CIRCLE_PROJECT_USERNAME/the-littlest-jupyterhub.git@$CIRCLE_SHA1 - fi - - .circleci/integration-test.py run-test \ - --installer-args "--admin admin:admin" \ - --bootstrap-pip-spec $BOOTSTRAP_PIP_SPEC \ - basic-tests test_admin_installer.py \ - << parameters.upgrade >> - - plugin_tests: - parameters: - upgrade: - type: string - default: "" - steps: - - run: - name: Run plugin tests - command: | - if [ $CIRCLE_PR_USERNAME ]; then - BOOTSTRAP_PIP_SPEC=git+https://github.com/$CIRCLE_PR_USERNAME/the-littlest-jupyterhub.git@$CIRCLE_SHA1 - else - BOOTSTRAP_PIP_SPEC=git+https://github.com/$CIRCLE_PROJECT_USERNAME/the-littlest-jupyterhub.git@$CIRCLE_SHA1 - fi - - .circleci/integration-test.py run-test \ - --bootstrap-pip-spec $BOOTSTRAP_PIP_SPEC \ - --installer-args "--plugin /srv/src/integration-tests/plugins/simplest" \ - plugins test_simplest_plugin.py \ - << parameters.upgrade >> - - bootstrap_checks: - parameters: - steps: - - run: - name: Run bootstrap checks - command: | - py.test integration-tests/test_bootstrap.py -s -