diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84bd05bc..c27055c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,3 +65,36 @@ jobs: cd engine/ pip install -r requirements.txt ./wait_for_test_mysql_start.sh && pytest --ds=settings.ci-test -x + + unit-test-backend-postgresql: + runs-on: ubuntu-latest + container: python:3.9 + env: + DB_BACKEND: postgresql + DJANGO_SETTINGS_MODULE: settings.ci-test + SLACK_CLIENT_OAUTH_ID: 1 + services: + rabbit_test: + image: rabbitmq:3.7.19 + env: + RABBITMQ_DEFAULT_USER: rabbitmq + RABBITMQ_DEFAULT_PASS: rabbitmq + postgresql_test: + image: postgres:14.4 + env: + POSTGRES_DB: oncall_local_dev + POSTGRES_PASSWORD: local_dev_pwd + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v2 + - name: Unit Test Backend + run: | + cd engine/ + pip install -r requirements.txt + pytest --ds=settings.ci-test -x + diff --git a/docker-compose-developer-pg.yml b/docker-compose-developer-pg.yml new file mode 100644 index 00000000..6be8ae2e --- /dev/null +++ b/docker-compose-developer-pg.yml @@ -0,0 +1,74 @@ +version: '3.2' + +services: + + postgres: + image: postgres:14.4 + platform: linux/x86_64 + mem_limit: 500m + cpus: 0.5 + restart: always + ports: + - 5432:5432 + environment: + POSTGRES_DB: oncall_local_dev + POSTGRES_PASSWORD: empty + POSTGRES_INITDB_ARGS: '--encoding=UTF-8' + + redis: + image: redis + mem_limit: 100m + cpus: 0.1 + restart: always + ports: + - 6379:6379 + + rabbit: + image: "rabbitmq:3.7.15-management" + mem_limit: 1000m + cpus: 0.5 + environment: + RABBITMQ_DEFAULT_USER: "rabbitmq" + RABBITMQ_DEFAULT_PASS: "rabbitmq" + RABBITMQ_DEFAULT_VHOST: "/" + ports: + - 15672:15672 + - 5672:5672 + + mysql-to-create-grafana-db: + image: mysql:5.7 + platform: linux/x86_64 + mem_limit: 500m + cpus: 0.5 + command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + restart: always + ports: + - 3306:3306 + environment: + MYSQL_ROOT_PASSWORD: empty + MYSQL_DATABASE: grafana + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] + timeout: 20s + retries: 10 + + grafana: + image: "grafana/grafana:9.0.0-beta3" + restart: always + mem_limit: 500m + cpus: 0.5 + environment: + GF_DATABASE_TYPE: mysql + GF_DATABASE_HOST: mysql + GF_DATABASE_USER: root + GF_DATABASE_PASSWORD: empty + GF_SECURITY_ADMIN_USER: oncall + GF_SECURITY_ADMIN_PASSWORD: oncall + GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app + volumes: + - ./grafana-plugin:/var/lib/grafana/plugins/grafana-plugin + ports: + - 3000:3000 + depends_on: + mysql-to-create-grafana-db: + condition: service_healthy diff --git a/docker-compose-developer.yml b/docker-compose-developer.yml index e35c3c70..d2889bbc 100644 --- a/docker-compose-developer.yml +++ b/docker-compose-developer.yml @@ -65,5 +65,5 @@ services: ports: - 3000:3000 depends_on: - mysql: + mysql-to-create-grafana-db: condition: service_healthy diff --git a/engine/requirements.txt b/engine/requirements.txt index d0896ae0..cc9eac99 100644 --- a/engine/requirements.txt +++ b/engine/requirements.txt @@ -40,6 +40,7 @@ https://github.com/iskhakov/django-push-notifications/archive/refs/tags/3.0.0-fi django-mirage-field==1.3.0 django-mysql==4.6.0 PyMySQL==1.0.2 +psycopg2-binary==2.9.3 emoji==1.7.0 apns2==0.7.2 diff --git a/engine/settings/base.py b/engine/settings/base.py index 578f0591..c1ee02d3 100644 --- a/engine/settings/base.py +++ b/engine/settings/base.py @@ -81,6 +81,24 @@ GRAFANA_CLOUD_ONCALL_TOKEN = os.environ.get("GRAFANA_CLOUD_ONCALL_TOKEN", None) # Outgoing webhook settings DANGEROUS_WEBHOOKS_ENABLED = getenv_boolean("DANGEROUS_WEBHOOKS_ENABLED", default=False) +# DB backend defaults +DB_BACKEND = os.environ.get("DB_BACKEND", "mysql") +DB_BACKEND_DEFAULT_VALUES = { + "mysql": { + "USER": "root", + "PORT": "3306", + "OPTIONS": { + "charset": "utf8mb4", + "connect_timeout": 1, + }, + }, + "postgresql": { + "USER": "postgres", + "PORT": "5432", + "OPTIONS": {}, + }, +} + # Application definition INSTALLED_APPS = [ diff --git a/engine/settings/ci-test.py b/engine/settings/ci-test.py index 16c655b5..f351d2c5 100644 --- a/engine/settings/ci-test.py +++ b/engine/settings/ci-test.py @@ -1,8 +1,5 @@ # flake8: noqa: F405 -# Workaround to use pymysql instead of mysqlclient -import pymysql - from .base import * # noqa SECRET_KEY = "u5/IIbuiJR3Y9FQMBActk+btReZ5oOxu+l8MIJQWLfVzESoan5REE6UNSYYEQdjBOcty9CDak2X" @@ -14,18 +11,23 @@ BASE_URL = "http://localhost" CELERY_BROKER_URL = "amqp://rabbitmq:rabbitmq@rabbit_test:5672" -pymysql.install_as_MySQLdb() +if DB_BACKEND == "mysql": + # Workaround to use pymysql instead of mysqlclient + import pymysql + + pymysql.install_as_MySQLdb() + DB_BACKEND_DEFAULT_VALUES[DB_BACKEND]["OPTIONS"] = {"charset": "utf8mb4"} + -# Primary database must have the name "default" DATABASES = { "default": { - "ENGINE": "django.db.backends.mysql", - "NAME": "oncall_local_dev", - "USER": "root", + "ENGINE": "django.db.backends.{}".format(DB_BACKEND), + "NAME": os.environ.get("DB_NAME", "oncall_local_dev"), + "USER": os.environ.get("DB_USER", DB_BACKEND_DEFAULT_VALUES.get(DB_BACKEND, {}).get("USER", "root")), "PASSWORD": "local_dev_pwd", - "HOST": "mysql_test", - "PORT": "3306", - "OPTIONS": {"charset": "utf8mb4"}, + "HOST": "{}_test".format(DB_BACKEND), + "PORT": os.environ.get("DB_PORT", DB_BACKEND_DEFAULT_VALUES.get(DB_BACKEND, {}).get("PORT", "3306")), + "OPTIONS": DB_BACKEND_DEFAULT_VALUES.get(DB_BACKEND, {}).get("OPTIONS", {}), }, } diff --git a/engine/settings/dev.py b/engine/settings/dev.py index b5e0e2f5..9dd65948 100644 --- a/engine/settings/dev.py +++ b/engine/settings/dev.py @@ -1,11 +1,26 @@ import os import sys -# Workaround to use pymysql instead of mysqlclient -import pymysql - from .base import * # noqa +if DB_BACKEND == "mysql": # noqa + # Workaround to use pymysql instead of mysqlclient + import pymysql + + pymysql.install_as_MySQLdb() + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.{}".format(DB_BACKEND), # noqa + "NAME": os.environ.get("DB_NAME", "oncall_local_dev"), + "USER": os.environ.get("DB_USER", DB_BACKEND_DEFAULT_VALUES.get(DB_BACKEND, {}).get("USER", "root")), # noqa + "PASSWORD": os.environ.get("DB_PASSWORD", "empty"), + "HOST": os.environ.get("DB_HOST", "127.0.0.1"), + "PORT": os.environ.get("DB_PORT", DB_BACKEND_DEFAULT_VALUES.get(DB_BACKEND, {}).get("PORT", "3306")), # noqa + "OPTIONS": DB_BACKEND_DEFAULT_VALUES.get(DB_BACKEND, {}).get("OPTIONS", {}), # noqa + }, +} + SECRET_KEY = os.environ.get("SECRET_KEY", "osMsNM0PqlRHBlUvqmeJ7+ldU3IUETCrY9TrmiViaSmInBHolr1WUlS0OFS4AHrnnkp1vp9S9z1") MIRAGE_SECRET_KEY = os.environ.get( @@ -13,26 +28,8 @@ MIRAGE_SECRET_KEY = os.environ.get( ) MIRAGE_CIPHER_IV = os.environ.get("MIRAGE_CIPHER_IV", "tZZa+60zTZO2NRcS") -pymysql.install_as_MySQLdb() - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.mysql", - "NAME": os.environ.get("MYSQL_DB_NAME", "oncall_local_dev"), - "USER": os.environ.get("MYSQL_USER", "root"), - "PASSWORD": os.environ.get("MYSQL_PASSWORD"), - "HOST": os.environ.get("MYSQL_HOST", "127.0.0.1"), - "PORT": os.environ.get("MYSQL_PORT", "3306"), - "OPTIONS": { - "charset": "utf8mb4", - "connect_timeout": 1, - }, - }, -} - TESTING = "pytest" in sys.modules or "unittest" in sys.modules - CACHES = { "default": { "BACKEND": "redis_cache.RedisCache",