diff --git a/.github/actions/gmt-pytest/action.yml b/.github/actions/gmt-pytest/action.yml new file mode 100644 index 00000000..fb662516 --- /dev/null +++ b/.github/actions/gmt-pytest/action.yml @@ -0,0 +1,55 @@ +name: 'GMT Pytest' +description: 'Run Pytest with setup and teardown' +inputs: + metrics-to-turn-off: + description: 'a list of metrics to turn off that is passed to the disable metrics script' + required: false + default: '' +runs: + using: 'composite' + steps: + - name: Install python dependencies + shell: bash + run: | + python -m pip install --upgrade pip + pip install fastapi pytest psycopg2 numpy pandas xgboost statsmodels scipy scikit-learn + + - name: Run Prereqs and Install Script + shell: bash + run: | + sudo apt install lm-sensors libsensors-dev libglib2.0-0 libglib2.0-dev + ./install.sh -p testpw + + - name: Turn off metric providers which do not work in github vm + shell: bash + run: | + ./disable-metric-providers.sh ${{ inputs.metrics-to-turn-off }} + + - name: Setup Test environment + shell: bash + working-directory: ./test + run: | + python3 setup-test-env.py + + - name: Start Test container + shell: bash + working-directory: ./test + run: | + docker compose -f ../docker/test-compose.yml up -d + + - name: Sleep for 10 seconds + run: sleep 10s + shell: bash + + - name: Run Tests + shell: bash + working-directory: ./test + run: | + pytest + + - name: Stop Containers + shell: bash + if: always() + working-directory: ./test + run: | + docker compose -f ../docker/test-compose.yml down -v \ No newline at end of file diff --git a/.github/workflows/tests-bare-metal.yml b/.github/workflows/tests-bare-metal.yml index 5cef5f6d..510b68a6 100644 --- a/.github/workflows/tests-bare-metal.yml +++ b/.github/workflows/tests-bare-metal.yml @@ -1,81 +1,65 @@ name: Daily Test Run - Bare Metal - +run-name: Scheduled - ${{ github.ref_name }} on: schedule: - cron: '0 0 * * *' branches: [ "main", "dev" ] - # Allows you to run this workflow manually from the Actions tab - #workflow_dispatch: - + #push: + # branch: test-pipeline-improvements jobs: - #TODO: is there a way to do this that's even more light-weight? check_date: runs-on: self-hosted name: Check latest commit outputs: - should_run: ${{ steps.check_gmt_commits.outputs.should_run }} + recent_dev_commit: ${{ steps.check_dev_commits.outputs.recent_dev_commit }} + recent_main_commit: ${{ steps.check_main_commits.outputs.recent_main_commit }} steps: - - uses: actions/checkout@v3 - - name: print latest_commit - run: echo ${{ github.sha }} + - id: check_dev_commits + continue-on-error: true + name: check latest dev commit is less than a day + if: ${{ github.event_name == 'schedule' }} + run: | + dateofcommit=$(curl -s https://api.github.com/repos/green-coding-berlin/green-metrics-tool/commits/dev | jq -r '.commit.author.date') + commit_timestamp=$(date --date "$dateofcommit" +'%s') + one_day_ago=$(date --date '1 day ago' +'%s') + [ $commit_timestamp -gt $one_day_ago ] && echo "recent_dev_commit=true" >> $GITHUB_OUTPUT - #TODO: instead of 24 hours, last workflow run - - id: check_gmt_commits + - id: check_main_commits continue-on-error: true - name: check latest commit is less than a day + name: check latest main commit is less than a day if: ${{ github.event_name == 'schedule' }} - run: test -n $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "should_run=true" >> $GITHUB_OUTPUT + run: | + dateofcommit=$(curl -s https://api.github.com/repos/green-coding-berlin/green-metrics-tool/commits/main | jq -r '.commit.author.date') + commit_timestamp=$(date --date "$dateofcommit" +'%s') + one_day_ago=$(date --date '1 day ago' +'%s') + [ $commit_timestamp -gt $one_day_ago ] && echo "recent_main_commit=true" >> $GITHUB_OUTPUT - run-tests: + run-tests-dev: needs: check_date - if: ${{ needs.check_date.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch' }} + if: ${{ needs.check_date.outputs.recent_dev_commit == 'true' }} runs-on: self-hosted - strategy: - fail-fast: false - matrix: - branch: ['dev', 'main'] steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 with: - ref: ${{ matrix.branch }} + ref: 'dev' submodules: 'true' - - name: Install python dependencies - run: | - python -m pip install --upgrade pip - pip install fastapi pytest psycopg2 numpy pandas xgboost statsmodels scipy scikit-learn - - - name: Run Prereqs and Install Script - run: | - sudo apt install lm-sensors libsensors-dev libglib2.0-dev - ./install.sh -p testpw - - - name: Turn off metric providers which do not work in github vm - run: | - ./disable-metric-providers.sh System Sensors Debug + - name: 'Setup, Run, and Teardown Tests' + uses: ./.github/actions/gmt-pytest + with: + metrics-to-turn-off: 'System Sensors Debug' - - name: Setup Test environment - working-directory: ./test - run: | - ./setup-test-env.sh -p testpw - - - name: Start Test container - working-directory: ./test - run: | - docker compose -f ../docker/test-compose.yml up -d - - - name: Sleep for 10 seconds - run: sleep 10s - shell: bash - - - name: Run Tests - working-directory: ./test - run: | - pytest - - - name: Stop Containers - if: always() - working-directory: ./test - run: | - docker compose -f ../docker/test-compose.yml down -v + run-tests-main: + needs: check_date + if: ${{ needs.check_date.outputs.recent_main_commit == 'true' }} + runs-on: self-hosted + steps: + - uses: actions/checkout@v3 + with: + ref: 'main' + submodules: 'true' + + - name: 'Setup, Run, and Teardown Tests' + uses: ./.github/actions/gmt-pytest + with: + metrics-to-turn-off: 'System Sensors Debug' diff --git a/.github/workflows/tests-manual.yml b/.github/workflows/tests-manual.yml new file mode 100644 index 00000000..eb6f619b --- /dev/null +++ b/.github/workflows/tests-manual.yml @@ -0,0 +1,19 @@ +name: Manual Test Run - Virtual Machine +run-name: Manual - ${{ github.ref_name }} +on: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + run-tests-manual: + runs-on: ubuntu-latest + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - name: 'Setup, Run, and Teardown Tests' + uses: ./.github/actions/gmt-pytest + with: + metrics-to-turn-off: 'RAPL System Sensors Debug CGroupV2' diff --git a/.github/workflows/tests-vm.yml b/.github/workflows/tests-vm.yml index 17c3e7b6..76de725b 100644 --- a/.github/workflows/tests-vm.yml +++ b/.github/workflows/tests-vm.yml @@ -1,81 +1,68 @@ name: Daily Test Run - Virtual Machine - +run-name: Scheduled - ${{ github.ref_name }} on: schedule: - cron: '0 0 * * *' branches: [ "main", "dev" ] - # Allows you to run this workflow manually from the Actions tab - #workflow_dispatch: + #push: + # branch: test-pipeline-improvements jobs: - #TODO: is there a way to do this that's even more light-weight? check_date: runs-on: ubuntu-latest name: Check latest commit outputs: - should_run: ${{ steps.check_gmt_commits.outputs.should_run }} + recent_dev_commit: ${{ steps.check_dev_commits.outputs.recent_dev_commit }} + recent_main_commit: ${{ steps.check_main_commits.outputs.recent_main_commit }} steps: - - uses: actions/checkout@v3 - - name: print latest_commit - run: echo ${{ github.sha }} + - id: check_dev_commits + continue-on-error: true + name: check latest dev commit is less than a day + if: ${{ github.event_name == 'schedule' }} + run: | + dateofcommit=$(curl -s https://api.github.com/repos/green-coding-berlin/green-metrics-tool/commits/dev | jq -r '.commit.author.date') + commit_timestamp=$(date --date "$dateofcommit" +'%s') + one_day_ago=$(date --date '1 day ago' +'%s') + [ $commit_timestamp -gt $one_day_ago ] && echo "recent_dev_commit=true" >> $GITHUB_OUTPUT - #TODO: instead of 24 hours, last workflow run - - id: check_gmt_commits + - id: check_main_commits continue-on-error: true - name: check latest commit is less than a day + name: check latest main commit is less than a day if: ${{ github.event_name == 'schedule' }} - run: test -n $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "should_run=true" >> $GITHUB_OUTPUT + run: | + dateofcommit=$(curl -s https://api.github.com/repos/green-coding-berlin/green-metrics-tool/commits/main | jq -r '.commit.author.date') + commit_timestamp=$(date --date "$dateofcommit" +'%s') + one_day_ago=$(date --date '1 day ago' +'%s') + [ $commit_timestamp -gt $one_day_ago ] && echo "recent_main_commit=true" >> $GITHUB_OUTPUT - run-tests: + run-tests-dev: needs: check_date - if: ${{ needs.check_date.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch' }} + if: ${{ needs.check_date.outputs.recent_dev_commit == 'true' }} runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - branch: ['dev', 'main'] steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v3 with: - ref: ${{ matrix.branch }} + ref: 'dev' submodules: 'true' - - name: Install python dependencies - run: | - python -m pip install --upgrade pip - pip install fastapi pytest psycopg2 numpy pandas xgboost statsmodels scipy scikit-learn - - - name: Run Prereqs and Install Script - run: | - sudo apt install lm-sensors libsensors-dev libglib2.0-dev - ./install.sh -p testpw - - - name: Turn off metric providers which do not work in github vm - run: | - ./disable-metric-providers.sh RAPL System Sensors Debug CGroupV2 + - name: 'Setup, Run, and Teardown Tests' + uses: ./.github/actions/gmt-pytest + with: + metrics-to-turn-off: 'RAPL System Sensors Debug CGroupV2' - - name: Setup Test environment - working-directory: ./test - run: | - ./setup-test-env.sh -p testpw - - - name: Start Test container - working-directory: ./test - run: | - docker compose -f ../docker/test-compose.yml up -d - - - name: Sleep for 10 seconds - run: sleep 10s - shell: bash - - - name: Run Tests - working-directory: ./test - run: | - pytest - - - name: Stop Containers - if: always() - working-directory: ./test - run: | - docker compose -f ../docker/test-compose.yml down -v + run-tests-main: + needs: check_date + if: ${{ needs.check_date.outputs.recent_main_commit == 'true' }} + runs-on: ubuntu-latest + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + with: + ref: 'main' + submodules: 'true' + + - name: 'Setup, Run, and Teardown Tests' + uses: ./.github/actions/gmt-pytest + with: + metrics-to-turn-off: 'RAPL System Sensors Debug CGroupV2' \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 1d896246..00000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Daily Test Run - VM - Manual - -on: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - run-tests: - runs-on: ubuntu-latest - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - with: - ref: ${{ matrix.branch }} - submodules: 'true' - - - name: Install python dependencies - run: | - python -m pip install --upgrade pip - pip install fastapi pytest psycopg2 numpy pandas xgboost statsmodels scipy scikit-learn - - - name: Run Prereqs and Install Script - run: | - sudo apt install lm-sensors libsensors-dev libglib2.0-dev - ./install.sh -p testpw - - - name: Turn off metric providers which do not work in github vm - run: | - ./disable-metric-providers.sh RAPL System Sensors Debug CGroupV2 - - - name: Setup Test environment - working-directory: ./test - run: | - ./setup-test-env.sh -p testpw - - - name: Start Test container - working-directory: ./test - run: | - docker compose -f ../docker/test-compose.yml up -d - - - name: Sleep for 10 seconds - run: sleep 10s - shell: bash - - - name: Run Tests - working-directory: ./test - run: | - pytest - - - name: Stop Containers - if: always() - working-directory: ./test - run: | - docker compose -f ../docker/test-compose.yml down -v diff --git a/README.md b/README.md index ee8f51bb..0a8ec9b2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Currently no special setup is done to isolate the containers from the host OS an Ensure that you run this only in an isolated VM / dedicated server. -![Tests Status](https://github.com/green-coding-berlin/green-metrics-tool/actions/workflows/tests.yml/badge.svg) +![Tests Status](https://github.com/green-coding-berlin/green-metrics-tool/actions/workflows/tests-vm.yml/badge.svg) # Introduction diff --git a/test/README.MD b/test/README.MD index f2d6dfd9..f3b6025c 100644 --- a/test/README.MD +++ b/test/README.MD @@ -7,9 +7,9 @@ We assume that your green-metrics-tool is already set up to work on your machine ## First time setup run: -`./setup-test-env.sh` +`python3 setup-test-env.py` -from the test directory. This will create a copy of the `config.yml` and docker compose.yml` files that will be used in the test containers. +from the test directory. This will create a copy of the `config.yml` and docker `compose.yml` files that will be used in the test containers. You will need to re-run this setup script if new metric providers are added or the config.yml is otherwise changed in a significant way. diff --git a/test/edit-etc-hosts.sh b/test/edit-etc-hosts.sh new file mode 100755 index 00000000..4cd73520 --- /dev/null +++ b/test/edit-etc-hosts.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -euo pipefail + +etc_hosts_line_1="127.0.0.1 test-green-coding-postgres-container" + +echo "Writing to /etc/hosts file..." +if ! sudo grep -Fxq "$etc_hosts_line_1" /etc/hosts; then + echo $etc_hosts_line_1 | sudo tee -a /etc/hosts +else + echo "Entry was already present..." +fi \ No newline at end of file diff --git a/test/setup-test-env.py b/test/setup-test-env.py new file mode 100644 index 00000000..e0d57725 --- /dev/null +++ b/test/setup-test-env.py @@ -0,0 +1,103 @@ +import yaml +import os +import re +from copy import deepcopy +import subprocess + +base_config_name = 'config.yml' +base_compose_name = 'compose.yml.example' +test_config_name = 'test-config.yml' +test_compose_name = 'test-compose.yml' + +db_pw = 'testpw' + +current_dir = os.path.abspath(os.path.dirname(__file__)) +base_config_path = os.path.join(current_dir, "../{base_config_name}".format(base_config_name=base_config_name)) +base_compose_path = os.path.join(current_dir, "../docker/{base_compose_name}".format(base_compose_name=base_compose_name)) +test_config_path = os.path.join(current_dir, "../{test_config_name}".format(test_config_name=test_config_name)) +test_compose_path = os.path.join(current_dir, "../docker/{test_compose_name}".format(test_compose_name=test_compose_name)) + +def edit_config_file(): + print("Creating test-config.yml...") + config = None + with open(base_config_path) as base_config_file: + config = yaml.load(base_config_file, Loader=yaml.FullLoader) + + # Reset SMTP + for e in config.get('smtp'): + config['smtp'][e] = None + + config['postgresql']['host'] = 'test-green-coding-postgres-container' + config['postgresql']['password'] = db_pw + config['admin']['no_emails'] = True + + with open(test_config_path, 'w') as test_config_file: + yaml.dump(config, test_config_file) + +def edit_compose_file(): + print("Creating test-compose.yml...") + compose = None + with open(base_compose_path) as base_compose_file: + compose = yaml.load(base_compose_file, Loader=yaml.FullLoader) + + # Save old volume names, as we will have to look for them under services/volumes soon + vol_keys = compose['volumes'].copy().keys() + + # Edit volume names with pre-pended 'test' + for e in compose.get('volumes').copy(): + compose['volumes']["test-{e}".format(e=e)] = deepcopy(compose['volumes'][e]) + del compose['volumes'][e] + + # Edit Services + for service in compose.get('services').copy(): + # Edit Services with new volumes + service_volumes = compose['services'][service]['volumes'] + new_vol_list=[] + for v in service_volumes: + for k in vol_keys: + v = v.replace(k,'test-{k}'.format(k=k)) + v = v.replace('PATH_TO_GREEN_METRICS_TOOL_REPO', '{cwd}/../'.format(cwd=current_dir)) + new_vol_list.append(v) + + ## Change the depends on: in services as well + if 'depends_on' in compose['services'][service]: + depends_on_list = compose['services'][service]['depends_on'] + new_depends_on_list=[] + for d in depends_on_list: + new_depends_on_list.append('test-{d}'.format(d=d)) + compose['services'][service]['depends_on'] = new_depends_on_list + + ## for nginx and gunicorn services, add test config mapping + if 'nginx' in service or 'gunicorn' in service: + new_vol_list.append('{cwd}/../test-config.yml:/var/www/green-metrics-tool/config.yml'.format(cwd=current_dir)) + compose['services'][service]['volumes'] = new_vol_list + + # For postgresql, change password + if 'postgres' in service: + new_env=[] + for e in compose['services'][service]['environment']: + e = e.replace('PLEASE_CHANGE_THIS', db_pw) + new_env.append(e) + compose['services'][service]['environment'] = new_env + + # Edit service container name + old_container_name = compose['services'][service]['container_name'] + compose['services'][service]['container_name'] = 'test-{name}'.format(name=old_container_name) + + # Edit service names with pre-pended 'test' + # Do this last so the changes done before are copied into new name + new_key = "test-{e}".format(e=service) + compose['services'][new_key] = deepcopy(compose['services'][service]) + del compose['services'][service] + + with open(test_compose_path, 'w') as test_compose_file: + yaml.dump(compose, test_compose_file) + +def edit_etc_hosts(): + subprocess.run(['./edit-etc-hosts.sh']) + +if __name__ == "__main__": + edit_config_file() + edit_compose_file() + edit_etc_hosts() + print('fin.') \ No newline at end of file diff --git a/test/setup-test-env.sh b/test/setup-test-env.sh deleted file mode 100755 index 0c38892d..00000000 --- a/test/setup-test-env.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -euo pipefail - -db_pw='' -while getopts "p:" o; do - case "$o" in - p) - db_pw=${OPTARG} - ;; - esac -done - -if [[ -z "$db_pw" ]] ; then - read -sp "Please enter the new password to be set for the testing PostgreSQL DB: " db_pw -fi - -echo "Updating config.yml with new password ..." -cp ../config.yml ../test-config.yml -sed -i -e "s|host: green-|host: test-|" ../test-config.yml -sed -i -e "s|PLEASE_CHANGE_THIS|$db_pw|" ../test-config.yml - -echo "Creating test-compose.yml ..." -cp ../docker/compose.yml.example ../docker/test-compose.yml -sed -i -e "s|PATH_TO_GREEN_METRICS_TOOL_REPO|$PWD/../|" ../docker/test-compose.yml -sed -i -e "s|PLEASE_CHANGE_THIS|$db_pw|" ../docker/test-compose.yml -sed -i -e "s|# - TEST_CONFIG_SETUP|- $PWD/../test-config.yml|" ../docker/test-compose.yml - -sed -i -e "s|container_name: green-|container_name: test-|" ../docker/test-compose.yml -sed -i -e "s|green-coding-postgres-data|green-coding-postgres-test-data|" ../docker/test-compose.yml - -etc_hosts_line_1="127.0.0.1 test-coding-postgres-container" - - -echo "Writing to /etc/hosts file..." -if ! sudo grep -Fxq "$etc_hosts_line_1" /etc/hosts; then - echo $etc_hosts_line_1 | sudo tee -a /etc/hosts -else - echo "Entry was already present..." -fi \ No newline at end of file diff --git a/test/start-test-containers.sh b/test/start-test-containers.sh index dad11da9..de664619 100755 --- a/test/start-test-containers.sh +++ b/test/start-test-containers.sh @@ -1,3 +1,3 @@ #!/bin/bash echo "Running docker containers..." -docker compose -f ../docker/test-compose.yml up \ No newline at end of file +docker compose -f ../docker/test-compose.yml up --remove-orphans \ No newline at end of file diff --git a/test/stop-test-containers.sh b/test/stop-test-containers.sh index bf862139..7098a49d 100755 --- a/test/stop-test-containers.sh +++ b/test/stop-test-containers.sh @@ -1,3 +1,3 @@ #!/bin/bash echo "Stopping docker containers..." -docker compose -f ../docker/test-compose.yml down -v \ No newline at end of file +docker compose -f ../docker/test-compose.yml down -v --remove-orphans \ No newline at end of file