From 6511e439c56b5c3d9f3d82811bd04abe57ca1da5 Mon Sep 17 00:00:00 2001 From: Ildar Iskhakov Date: Tue, 9 Jul 2024 09:24:18 +0800 Subject: [PATCH] Add tilt button to run pytest, fix local dev config for pytest, minor improvements to til (#3927) # What this PR does Fixes for https://github.com/grafana/oncall-private/issues/2423 ## Which issue(s) this PR fixes ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) --------- Co-authored-by: Joey Orlando Co-authored-by: Dominik --- Tiltfile | 36 ++++++++++++++++++- dev/helm-local.yml | 16 +++++++++ .../tests/test_alert_receiver_channel.py | 10 ++++++ engine/apps/integrations/tasks.py | 4 ++- engine/apps/webhooks/tests/test_webhook.py | 6 ++++ engine/settings/dev.py | 7 ++++ engine/settings/prod_without_db.py | 2 -- 7 files changed, 77 insertions(+), 4 deletions(-) diff --git a/Tiltfile b/Tiltfile index a2cd3a51..f6e07226 100644 --- a/Tiltfile +++ b/Tiltfile @@ -83,7 +83,7 @@ if not is_ci: local_resource( "e2e-tests", - labels=["E2eTests"], + labels=["allTests"], cmd=e2e_tests_cmd, trigger_mode=TRIGGER_MODE_MANUAL, auto_init=is_ci, @@ -127,6 +127,34 @@ cmd_button( icon_name="dangerous", ) +# Inspired by https://github.com/grafana/slo/blob/main/Tiltfile#L72 +pod_engine_pytest_script = ''' +set -eu +# get engine k8s pod name from tilt resource name +POD_NAME="$(tilt get kubernetesdiscovery "engine" -ojsonpath='{.status.pods[0].name}')" +kubectl exec "$POD_NAME" -- pytest . $STOP_ON_FIRST_FAILURE $TESTS_FILTER +''' +local_resource( + "pytest-tests", + labels=["allTests"], + cmd=['sh', '-c', pod_engine_pytest_script], + trigger_mode=TRIGGER_MODE_MANUAL, + auto_init=False, + resource_deps=["engine"] +) + +cmd_button( + name="pytest Tests - headless run", + argv=['sh', '-c', pod_engine_pytest_script], + text="Run pytest", + resource="pytest-tests", + icon_name="replay", + inputs=[ + text_input("TESTS_FILTER", "pytest optional arguments (e.g. \"apps/webhooks/tests/test_webhook.py::test_build_url_private_raises\")", "", "Test file names to run"), + bool_input("STOP_ON_FIRST_FAILURE", "Stop on first failure", True, "-x", ""), + ] +) + helm_oncall_values = ["./dev/helm-local.yml", "./dev/helm-local.dev.yml"] if is_ci: helm_oncall_values = helm_oncall_values + ["./.github/helm-ci.yml"] @@ -174,7 +202,10 @@ k8s_resource( resource_deps=["mariadb", "redis-master"], labels=["OnCallBackend"], ) +k8s_resource(workload="engine-migrate", labels=["OnCallBackend"]) + k8s_resource(workload="redis-master", labels=["OnCallDeps"]) +k8s_resource(workload="prometheus-server", labels=["OnCallDeps"]) k8s_resource( workload="mariadb", port_forwards='3307:3306', # : @@ -184,6 +215,9 @@ k8s_resource( # name all tilt resources after the k8s object namespace + name def resource_name(id): + # Remove variable date from job name + if id.name.startswith(HELM_PREFIX + "-engine-migrate"): + return "engine-migrate" return id.name.replace(HELM_PREFIX + "-", "") workload_to_resource_function(resource_name) diff --git a/dev/helm-local.yml b/dev/helm-local.yml index 721bd3c6..02a70527 100644 --- a/dev/helm-local.yml +++ b/dev/helm-local.yml @@ -5,6 +5,14 @@ env: value: "False" - name: FEATURE_PROMETHEUS_EXPORTER_ENABLED value: "True" + - name: DJANGO_SETTINGS_MODULE + value: "settings.dev" + - name: FEATURE_TELEGRAM_INTEGRATION_ENABLED + value: "True" + - name: FEATURE_SLACK_INTEGRATION_ENABLED + value: "True" + - name: SLACK_SLASH_COMMAND_NAME + value: "/oncall" # enabled to be able to test docker.host.internal in the webhook e2e tests - name: DANGEROUS_WEBHOOKS_ENABLED value: "True" @@ -131,6 +139,14 @@ service: nodePort: 30001 prometheus: enabled: true + alertmanager: + enabled: false + kube-state-metrics: + enabled: false + prometheus-node-exporter: + enabled: false + prometheus-pushgateway: + enabled: false server: global: scrape_interval: 10s diff --git a/engine/apps/alerts/tests/test_alert_receiver_channel.py b/engine/apps/alerts/tests/test_alert_receiver_channel.py index 1a2923f3..282b8c18 100644 --- a/engine/apps/alerts/tests/test_alert_receiver_channel.py +++ b/engine/apps/alerts/tests/test_alert_receiver_channel.py @@ -1,7 +1,9 @@ +import os from unittest import mock from unittest.mock import patch import pytest +from django.conf import settings from django.db import IntegrityError from django.urls import reverse from django.utils import timezone @@ -10,6 +12,7 @@ from apps.alerts.models import AlertReceiveChannel from common.api_helpers.utils import create_engine_url from common.exceptions import UnableToSendDemoAlert from engine.management.commands import alertmanager_v2_migrate +from settings.base import DatabaseTypes @pytest.mark.django_db @@ -272,6 +275,13 @@ def test_create_missing_direct_paging_integrations( def test_create_duplicate_direct_paging_integrations(make_organization, make_team, make_alert_receive_channel): """Check that it's not possible to have more than one active direct paging integration per team.""" + # MariaDB is not supported for this test + # See comment: https://github.com/grafana/oncall/commit/381a9ecf54bf0dd076f233b207c13d72ed792181#diff-9d96504027309f2bd1e95352bac1433b09b60eb4fafb611b52a6c15ed16cbc48R219-R223 + is_local_dev_env = os.environ.get("DJANGO_SETTINGS_MODULE") == "settings.dev" + is_db_type_mysql = settings.DATABASE_TYPE == DatabaseTypes.MYSQL + if is_local_dev_env and is_db_type_mysql: + pytest.skip("This test is not supported by Mariadb (used by settings.dev)") + organization = make_organization() team = make_team(organization) make_alert_receive_channel(organization, team=team, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING) diff --git a/engine/apps/integrations/tasks.py b/engine/apps/integrations/tasks.py index 3d9a77ad..0ee5acbb 100644 --- a/engine/apps/integrations/tasks.py +++ b/engine/apps/integrations/tasks.py @@ -132,7 +132,9 @@ def create_alert( }, countdown=countdown, ) - logger.warning(f"Retrying the task gracefully in {countdown} seconds due to ConcurrentUpdateError") + logger.warning( + f"Retrying the task gracefully in {countdown} seconds due to ConcurrentUpdateError for alert_receive_channel={alert_receive_channel_pk}" + ) @shared_dedicated_queue_retry_task() diff --git a/engine/apps/webhooks/tests/test_webhook.py b/engine/apps/webhooks/tests/test_webhook.py index 7e86dc02..389c0702 100644 --- a/engine/apps/webhooks/tests/test_webhook.py +++ b/engine/apps/webhooks/tests/test_webhook.py @@ -168,6 +168,9 @@ def test_build_url_invalid_url(make_organization, make_custom_webhook): @pytest.mark.django_db def test_build_url_private_raises(make_organization, make_custom_webhook): + if settings.DANGEROUS_WEBHOOKS_ENABLED: + pytest.skip("Dangerous webhooks are enabled") + organization = make_organization() webhook = make_custom_webhook(organization=organization, url="{{foo}}") @@ -241,6 +244,9 @@ def test_make_request(make_organization, make_custom_webhook): @httpretty.activate(verbose=True, allow_net_connect=False) @pytest.mark.django_db def test_make_request_bad_redirect(make_organization, make_custom_webhook): + if settings.DANGEROUS_WEBHOOKS_ENABLED: + pytest.skip("Dangerous webhooks are enabled") + organization = make_organization() webhook = make_custom_webhook(organization=organization, http_method="POST") diff --git a/engine/settings/dev.py b/engine/settings/dev.py index 087f013f..078b0735 100644 --- a/engine/settings/dev.py +++ b/engine/settings/dev.py @@ -66,6 +66,13 @@ if TESTING: TELEGRAM_TOKEN = "0000000000:XXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXX" TWILIO_AUTH_TOKEN = "twilio_auth_token" + # charset/collation related tests don't work without this + TEST_SETTINGS = { + "CHARSET": "utf8mb4", + "COLLATION": "utf8mb4_unicode_ci", + } + DATABASES["default"]["TEST"] = TEST_SETTINGS + INTERNAL_IPS = [ "127.0.0.1", ] diff --git a/engine/settings/prod_without_db.py b/engine/settings/prod_without_db.py index f505c8f9..ced1f3e9 100644 --- a/engine/settings/prod_without_db.py +++ b/engine/settings/prod_without_db.py @@ -16,11 +16,9 @@ except ModuleNotFoundError: # Only works under uwsgi web server environment pass - SLACK_SIGNING_SECRET = os.environ.get("SLACK_SIGNING_SECRET") SLACK_SIGNING_SECRET_LIVE = os.environ.get("SLACK_SIGNING_SECRET_LIVE", "") - STATICFILES_DIRS = [ "/etc/app/static", ]