diff --git a/.github/workflows/test_local_integration.yaml b/.github/workflows/test_local_integration.yaml index 7d79efc42..f8c24915e 100644 --- a/.github/workflows/test_local_integration.yaml +++ b/.github/workflows/test_local_integration.yaml @@ -4,6 +4,7 @@ env: TEST_USERNAME: "test-user" TEST_PASSWORD: "P@sswo3d" NEBARI_IMAGE_TAG: "main" + PYTHON_VERSION: "3.11" on: pull_request: @@ -70,7 +71,7 @@ jobs: CONDA: /home/runnerx/miniconda3 with: auto-update-conda: true - python-version: "3.11" + python-version: ${{ env.PYTHON_VERSION }} miniconda-version: "latest" - name: Install JQ @@ -99,7 +100,7 @@ jobs: - name: Create example-user working-directory: ${{ steps.init.outputs.directory }} run: | - nebari keycloak adduser --user "${TEST_USERNAME}" "${TEST_PASSWORD}" --config ${{ steps.init.outputs.config }} + nebari keycloak adduser --user "${TEST_USERNAME}" "${TEST_PASSWORD}" --config ${{ steps.init.outputs.config }} --groups superadmin nebari keycloak listusers --config ${{ steps.init.outputs.config }} - name: Await Workloads @@ -111,38 +112,97 @@ jobs: max-restarts: 3 ### DEPLOYMENT TESTS - - name: Deployment Pytests + # - name: Deployment Pytests + # env: + # NEBARI_CONFIG_PATH: ${{ steps.init.outputs.config }} + # KEYCLOAK_USERNAME: ${{ env.TEST_USERNAME }} + # KEYCLOAK_PASSWORD: ${{ env.TEST_PASSWORD }} + # run: | + # pytest tests/tests_deployment/ -v -s + + # ### USER-JOURNEY TESTS + # - uses: actions/setup-node@v4 + # with: + # node-version: 20 + + # - name: Playwright Tests + # env: + # KEYCLOAK_USERNAME: ${{ env.TEST_USERNAME }} + # KEYCLOAK_PASSWORD: ${{ env.TEST_PASSWORD }} + # NEBARI_FULL_URL: "https://${{ steps.init.outputs.domain }}/" + # working-directory: tests/tests_e2e/playwright + # run: | + # # create environment file + # envsubst < .env.tpl > .env + # # run playwright pytest tests in headed mode with the chromium browser + # xvfb-run pytest --browser chromium --slowmo 300 --headed + + # - name: Save Playwright recording artifacts + # if: always() + # uses: actions/upload-artifact@v4.3.1 + # with: + # name: e2e-playwright + # path: | + # ./tests/tests_e2e/playwright/videos/ + + ### CONDA-STORE TESTS + - name: "Checkout conda-store" + uses: actions/checkout@main + with: + fetch-depth: 0 + repository: conda-incubator/conda-store + ref: main + path: conda-store + + - name: "Set up conda-store conda env" + uses: conda-incubator/setup-miniconda@v3 env: - NEBARI_CONFIG_PATH: ${{ steps.init.outputs.config }} + CONDA: /home/runnerx/miniconda3 + with: + environment-file: conda-store/conda-store-server/environment-dev.yaml + miniforge-version: latest + auto-activate-base: false + activate-environment: conda-store-server-dev + python-version: ${{ env.PYTHON_VERSION }} + conda-remove-defaults: "true" + + - name: Install conda-store dependencies + run: | + which python + # install conda-store-server + python -m pip install conda-store/conda-store-server + + - name: Get conda store token from login + id: conda-store-token + env: + CONDA_STORE_BASE_URL: https://${{ steps.init.outputs.domain }} KEYCLOAK_USERNAME: ${{ env.TEST_USERNAME }} KEYCLOAK_PASSWORD: ${{ env.TEST_PASSWORD }} + working-directory: ${{ steps.init.outputs.directory }} run: | - pytest tests/tests_deployment/ -v -s - - ### USER-JOURNEY TESTS - - uses: actions/setup-node@v4 - with: - node-version: 20 + echo "${KEYCLOAK_USERNAME}" + LOGIN_URL=$(curl -c /tmp/nebari-session --insecure -Ls ${CONDA_STORE_BASE_URL}/conda-store/login/\?next= | grep auth/realms/nebari/login-actions/authenticate | grep -oP '(?<=action=")([^"]+)' | sed 's/\&\;/\&/g') + curl -X POST --insecure -b /tmp/nebari-session -c /tmp/conda-store-cookie -H "Content-Type: application/x-www-form-urlencoded" --data '{"credentialId":"", "username": "test-user", "password": "P@sswo3d"}' $LOGIN_URL + curl --insecure -b /tmp/conda-store-cookie ${CONDA_STORE_BASE_URL}/conda-store/api/v1/permission/ | jq + echo "CONDA_STORE_TOKEN=$(curl --insecure -L -X POST -b /tmp/conda-store-cookie ${CONDA_STORE_BASE_URL}/conda-store/api/v1/token | jq --raw-output .data.token)" >> "$GITHUB_OUTPUT" - - name: Playwright Tests + - name: Run conda-store-server user_journey tests env: + NEBARI_CONFIG_PATH: ${{ steps.init.outputs.config }} KEYCLOAK_USERNAME: ${{ env.TEST_USERNAME }} KEYCLOAK_PASSWORD: ${{ env.TEST_PASSWORD }} - NEBARI_FULL_URL: "https://${{ steps.init.outputs.domain }}/" - working-directory: tests/tests_e2e/playwright + CONDA_STORE_BASE_URL: https://${{ steps.init.outputs.domain }} + CONDA_STORE_TEST_VERIFY_SSL: 0 + CONDA_STORE_TOKEN: ${{ steps.conda-store-token.outputs.CONDA_STORE_TOKEN }} run: | - # create environment file - envsubst < .env.tpl > .env - # run playwright pytest tests in headed mode with the chromium browser - xvfb-run pytest --browser chromium --slowmo 300 --headed - - - name: Save Playwright recording artifacts - if: always() - uses: actions/upload-artifact@v4.3.1 - with: - name: e2e-playwright - path: | - ./tests/tests_e2e/playwright/videos/ + curl --insecure --header "Authorization: Bearer ${CONDA_STORE_TOKEN}" ${CONDA_STORE_BASE_URL}/conda-store/api/v1/permission/ | jq + cd conda-store/conda-store-server + python -m pytest -m "user_journey" + + - name: Get conda-store-server logs + if: ${{ failure() }} + run: | + kubectl logs -n dev deployment/nebari-conda-store-server ### CLEANUP AFTER TESTS - name: Cleanup nebari deployment diff --git a/src/_nebari/keycloak.py b/src/_nebari/keycloak.py index 6bfea9b8b..a1855b8a3 100644 --- a/src/_nebari/keycloak.py +++ b/src/_nebari/keycloak.py @@ -29,7 +29,10 @@ def do_keycloak(config: schema.Main, *args): username = args[1] password = args[2] if len(args) >= 3 else None - create_user(keycloak_admin, username, password, domain=config.domain) + groups = args[3] if len(args) >= 4 else None + create_user( + keycloak_admin, username, password, domain=config.domain, groups=groups + ) elif args[0] == "listusers": list_users(keycloak_admin) else: diff --git a/src/_nebari/subcommands/keycloak.py b/src/_nebari/subcommands/keycloak.py index 8f57d3417..b200c656e 100644 --- a/src/_nebari/subcommands/keycloak.py +++ b/src/_nebari/subcommands/keycloak.py @@ -1,8 +1,9 @@ import json import pathlib -from typing import Tuple +from typing import List, Tuple import typer +from typing_extensions import Annotated from _nebari.config import read_configuration from _nebari.keycloak import do_keycloak, export_keycloak_users @@ -36,11 +37,18 @@ def add_user( "--config", help="nebari configuration file path", ), + groups: Annotated[ + List[str], + typer.Option( + "--groups", + help="Role to give the user. Can be specified multiple times for multiple groups", + ), + ] = None, ): """Add a user to Keycloak. User will be automatically added to the [italic]analyst[/italic] group.""" from nebari.plugins import nebari_plugin_manager - args = ["adduser", add_users[0], add_users[1]] + args = ["adduser", add_users[0], add_users[1], groups] config_schema = nebari_plugin_manager.config_schema config = read_configuration(config_filename, config_schema) do_keycloak(config, *args)