diff --git a/CHANGELOG.md b/CHANGELOG.md index b3a44fab..d7723287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Public API for webhooks @mderynck ([#2790](https://github.com/grafana/oncall/pull/2790)) +- Use Telegram polling protocol instead of a webhook if `FEATURE_TELEGRAM_LONG_POLLING_ENABLED` set to `True` by @alexintech + ([#2250](https://github.com/grafana/oncall/pull/2250)) ### Changed diff --git a/Makefile b/Makefile index f42b3b85..14f26c0b 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ REDIS_PROFILE = redis RABBITMQ_PROFILE = rabbitmq PROMETHEUS_PROFILE = prometheus GRAFANA_PROFILE = grafana +TELEGRAM_POLLING_PROFILE = telegram_polling DEV_ENV_DIR = ./dev DEV_ENV_FILE = $(DEV_ENV_DIR)/.env.dev diff --git a/dev/.env.dev.example b/dev/.env.dev.example index 9a041dc2..1a37d99b 100644 --- a/dev/.env.dev.example +++ b/dev/.env.dev.example @@ -27,7 +27,7 @@ SOCIAL_AUTH_REDIRECT_IS_HTTPS=False GRAFANA_INCIDENT_STATIC_API_KEY= GRAFANA_API_URL=http://localhost:3000 -CELERY_WORKER_QUEUE="default,critical,long,slack,telegram,webhook,retry,celery" +CELERY_WORKER_QUEUE=default,critical,long,slack,telegram,webhook,retry,celery CELERY_WORKER_CONCURRENCY=3 CELERY_WORKER_MAX_TASKS_PER_CHILD=100 CELERY_WORKER_SHUTDOWN_INTERVAL=65m diff --git a/dev/README.md b/dev/README.md index a800b8c3..71751115 100644 --- a/dev/README.md +++ b/dev/README.md @@ -75,6 +75,7 @@ The possible profiles values are: - `rabbitmq` - `postgres` - `mysql` +- `telegram_polling` The default is `engine,oncall_ui,redis,grafana`. This runs: diff --git a/docker-compose-developer.yml b/docker-compose-developer.yml index b419b445..f6d62e8f 100644 --- a/docker-compose-developer.yml +++ b/docker-compose-developer.yml @@ -81,6 +81,23 @@ services: profiles: - engine + oncall_telegram_polling: + container_name: oncall_telegram_polling + labels: *oncall-labels + build: *oncall-build-args + restart: always + user: *oncall-user + command: sh -c "python manage.py start_telegram_polling" + env_file: *oncall-env-files + environment: *oncall-env-vars + volumes: *oncall-volumes + extra_hosts: *oncall-extra-hosts + depends_on: + oncall_db_migration: + condition: service_completed_successfully + profiles: + - telegram_polling + # used to invoke one-off commands, primarily from the Makefile # oncall_engine couldn't (easily) be used due to it's depends_on property # we could alternatively just use `docker run` however that would require diff --git a/docs/sources/open-source/_index.md b/docs/sources/open-source/_index.md index c42b8174..011b3058 100644 --- a/docs/sources/open-source/_index.md +++ b/docs/sources/open-source/_index.md @@ -178,18 +178,20 @@ frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media web-share" allowfullscreen> The Telegram integration for Grafana OnCall is designed for collaborative team work and improved incident response. +It's available in two options: using Webhooks or Long Polling. Refer to the following steps to configure the Telegram integration: 1. Ensure your Grafana OnCall environment is up and running. 2. Set `FEATURE_TELEGRAM_INTEGRATION_ENABLED` as "True" -3. Create a Telegram bot using [BotFather](https://t.me/BotFather) and save the token provided by BotFather. Please make +3. (Long Polling only) Set `FEATURE_TELEGRAM_LONG_POLLING_ENABLED` as "True" +4. Create a Telegram bot using [BotFather](https://t.me/BotFather) and save the token provided by BotFather. Please make sure to disable **Group Privacy** for the bot (Bot Settings -> Group Privacy -> Turn off). -4. Paste the token provided by BotFather to the `TELEGRAM_TOKEN` variable on the **Env Variables** page of your +5. Paste the token provided by BotFather to the `TELEGRAM_TOKEN` variable on the **Env Variables** page of your Grafana OnCall instance. -5. Set the `TELEGRAM_WEBHOOK_HOST` variable to the external address of your Grafana OnCall instance. Please note +6. (Webhook only) Set the `TELEGRAM_WEBHOOK_HOST` variable to the external address of your Grafana OnCall instance. Please note that `TELEGRAM_WEBHOOK_HOST` must start with `https://` and be publicly available (meaning that it can be reached by Telegram servers). If your host is private or local, consider using a reverse proxy (e.g. [ngrok](https://ngrok.com)). -6. Now you can connect Telegram accounts on the **Users** page and receive alert groups to Telegram direct messages. +7. Now you can connect Telegram accounts on the **Users** page and receive alert groups to Telegram direct messages. Alternatively, in case you want to connect Telegram channels to your Grafana OnCall environment, navigate to the **ChatOps** tab. diff --git a/engine/apps/api/tests/test_live_settings.py b/engine/apps/api/tests/test_live_settings.py index dce86236..db069358 100644 --- a/engine/apps/api/tests/test_live_settings.py +++ b/engine/apps/api/tests/test_live_settings.py @@ -160,3 +160,33 @@ def test_live_settings_telegram_calls_set_webhook_once( mock_set_webhook.assert_called_once_with( "TEST_UPDATED_VALUE/telegram/", allowed_updates=("message", "callback_query") ) + + +@pytest.mark.django_db +def test_live_settings_telegram_set_webhook_not_called_if_long_polling_enabled( + make_organization_and_user_with_plugin_token, + make_user_auth_headers, + make_live_setting, + settings, +): + """ + Check that when FEATURE_TELEGRAM_LONG_POLLING_ENABLED is true setting webhook with updating + TELEGRAM_WEBHOOK_HOST live setting does not evaluate. + """ + + settings.FEATURE_LIVE_SETTINGS_ENABLED = True + settings.FEATURE_TELEGRAM_LONG_POLLING_ENABLED = True + + organization, user, token = make_organization_and_user_with_plugin_token() + LiveSetting.populate_settings_if_needed() + live_setting = LiveSetting.objects.get(name="TELEGRAM_WEBHOOK_HOST") + + client = APIClient() + url = reverse("api-internal:live_settings-detail", kwargs={"pk": live_setting.public_primary_key}) + data = {"id": live_setting.public_primary_key, "value": "TEST_UPDATED_VALUE", "name": "TELEGRAM_WEBHOOK_HOST"} + + with mock.patch("telegram.Bot.set_webhook") as mock_set_webhook: + response = client.put(url, data=data, format="json", **make_user_auth_headers(user, token)) + + assert response.status_code == HTTP_200_OK + mock_set_webhook.assert_not_called() diff --git a/engine/apps/base/utils.py b/engine/apps/base/utils.py index 39eec6c5..ec15ac41 100644 --- a/engine/apps/base/utils.py +++ b/engine/apps/base/utils.py @@ -3,6 +3,7 @@ import re from urllib.parse import urlparse import phonenumbers +from django.conf import settings from phonenumbers import NumberParseException from telegram import Bot from twilio.base.exceptions import TwilioException @@ -137,6 +138,9 @@ class LiveSettingValidator: @classmethod def _check_telegram_webhook_host(cls, telegram_webhook_host): + if settings.FEATURE_TELEGRAM_LONG_POLLING_ENABLED: + return + try: # avoid circular import from apps.telegram.client import TelegramClient diff --git a/engine/apps/telegram/client.py b/engine/apps/telegram/client.py index 33e70072..8bd547b2 100644 --- a/engine/apps/telegram/client.py +++ b/engine/apps/telegram/client.py @@ -46,6 +46,13 @@ class TelegramClient: self.api_client.set_webhook(webhook_url, allowed_updates=self.ALLOWED_UPDATES) + def delete_webhook(self): + webhook_info = self.api_client.get_webhook_info() + if webhook_info.url == "": + return + + self.api_client.delete_webhook() + def send_message( self, chat_id: Union[int, str], diff --git a/engine/apps/telegram/tasks.py b/engine/apps/telegram/tasks.py index a650ad01..54b84945 100644 --- a/engine/apps/telegram/tasks.py +++ b/engine/apps/telegram/tasks.py @@ -28,6 +28,9 @@ logger.setLevel(logging.DEBUG) ) @handle_missing_token def register_telegram_webhook(token=None): + if settings.FEATURE_TELEGRAM_LONG_POLLING_ENABLED: + return + telegram_client = TelegramClient(token=token) try: diff --git a/engine/engine/celery.py b/engine/engine/celery.py index f0159207..49e1d0a3 100644 --- a/engine/engine/celery.py +++ b/engine/engine/celery.py @@ -57,7 +57,8 @@ def on_after_setup_logger(logger, **kwargs): def on_worker_ready(*args, **kwargs): from apps.telegram.tasks import register_telegram_webhook - register_telegram_webhook.delay() + if not settings.FEATURE_TELEGRAM_LONG_POLLING_ENABLED: + register_telegram_webhook.delay() if settings.OTEL_TRACING_ENABLED and settings.OTEL_EXPORTER_OTLP_ENDPOINT: diff --git a/engine/engine/management/commands/start_telegram_polling.py b/engine/engine/management/commands/start_telegram_polling.py new file mode 100644 index 00000000..4d15b434 --- /dev/null +++ b/engine/engine/management/commands/start_telegram_polling.py @@ -0,0 +1,50 @@ +import logging + +import telegram.error +from django.core.management.base import BaseCommand +from telegram.ext import CallbackQueryHandler, Filters, MessageHandler, Updater + +from apps.telegram.client import TelegramClient +from apps.telegram.updates.update_manager import UpdateManager + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +def start_telegram_polling(): + telegram_client = TelegramClient() + + telegram_client.delete_webhook() + + updater = Updater(token=telegram_client.token, use_context=True) + + # Register the error handler function with the dispatcher + updater.dispatcher.add_error_handler(error_handler) + + callback_handler = CallbackQueryHandler(handle_message) + + # register the message handler function with the dispatcher + updater.dispatcher.add_handler(MessageHandler(Filters.text, handle_message)) + updater.dispatcher.add_handler(callback_handler) + + # start the long polling loop + updater.start_polling() + + +def error_handler(update, context): + try: + raise context.error + except telegram.error.Conflict as e: + logger.warning(f"Tried to getUpdates() using telegram long polling, but conflict exists, got error: {e}") + + +def handle_message(update, context): + logger.debug(f"Update from Telegram: {update}") + + UpdateManager.process_update(update) + + +class Command(BaseCommand): + def handle(self, *args, **options): + logger.info("Starting telegram polling...") + start_telegram_polling() diff --git a/engine/settings/base.py b/engine/settings/base.py index 0522be84..c2d4f13b 100644 --- a/engine/settings/base.py +++ b/engine/settings/base.py @@ -59,6 +59,7 @@ BASE_URL = os.environ.get("BASE_URL") # Root URL of OnCall backend # Feature toggles FEATURE_LIVE_SETTINGS_ENABLED = getenv_boolean("FEATURE_LIVE_SETTINGS_ENABLED", default=True) FEATURE_TELEGRAM_INTEGRATION_ENABLED = getenv_boolean("FEATURE_TELEGRAM_INTEGRATION_ENABLED", default=True) +FEATURE_TELEGRAM_LONG_POLLING_ENABLED = getenv_boolean("FEATURE_TELEGRAM_LONG_POLLING_ENABLED", default=False) FEATURE_EMAIL_INTEGRATION_ENABLED = getenv_boolean("FEATURE_EMAIL_INTEGRATION_ENABLED", default=True) FEATURE_SLACK_INTEGRATION_ENABLED = getenv_boolean("FEATURE_SLACK_INTEGRATION_ENABLED", default=True) FEATURE_MULTIREGION_ENABLED = getenv_boolean("FEATURE_MULTIREGION_ENABLED", default=False) diff --git a/helm/oncall/README.md b/helm/oncall/README.md index e55bc6fa..a4eaa782 100644 --- a/helm/oncall/README.md +++ b/helm/oncall/README.md @@ -214,7 +214,7 @@ oncall: `oncall.slack.commandName` is used for changing default bot slash command, `oncall`. In slack, it could be called via `/`. -To set up Telegram tokem and webhook url use: +To set up Telegram token and webhook url use: ```yaml oncall: @@ -224,6 +224,13 @@ oncall: webhookUrl: ~ ``` +To use Telegram long polling instead of webhook use: + +```yaml +telegramPolling: + enabled: true +``` + ### Set up external access Grafana OnCall can be connected to the external monitoring systems or grafana deployed to the other cluster. diff --git a/helm/oncall/templates/_env.tpl b/helm/oncall/templates/_env.tpl index 0999a13e..57b69500 100644 --- a/helm/oncall/templates/_env.tpl +++ b/helm/oncall/templates/_env.tpl @@ -95,9 +95,16 @@ {{- end }} {{- define "snippet.oncall.telegram.env" -}} +{{- if .Values.telegramPolling.enabled -}} +{{- $_ := set .Values.oncall.telegram "enabled" true -}} +{{- end -}} - name: FEATURE_TELEGRAM_INTEGRATION_ENABLED value: {{ .Values.oncall.telegram.enabled | toString | title | quote }} {{- if .Values.oncall.telegram.enabled }} +{{- if .Values.telegramPolling.enabled }} +- name: FEATURE_TELEGRAM_LONG_POLLING_ENABLED + value: {{ .Values.telegramPolling.enabled | toString | title | quote }} +{{- end }} - name: TELEGRAM_WEBHOOK_HOST value: {{ .Values.oncall.telegram.webhookUrl | default (printf "https://%s" .Values.base_url) | quote }} {{- if .Values.oncall.telegram.existingSecret }} diff --git a/helm/oncall/templates/telegram-polling/_helpers.tpl b/helm/oncall/templates/telegram-polling/_helpers.tpl new file mode 100644 index 00000000..d2053dc0 --- /dev/null +++ b/helm/oncall/templates/telegram-polling/_helpers.tpl @@ -0,0 +1,22 @@ +{{/* +Maximum of 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "oncall.telegramPolling.fullname" -}} +{{ include "oncall.fullname" . | trunc 45 }}-telegram-polling +{{- end }} + +{{/* +Telegram polling common labels +*/}} +{{- define "oncall.telegramPolling.labels" -}} +{{ include "oncall.labels" . }} +app.kubernetes.io/component: telegram-polling +{{- end }} + +{{/* +Telegram polling selector labels +*/}} +{{- define "oncall.telegramPolling.selectorLabels" -}} +{{ include "oncall.selectorLabels" . }} +app.kubernetes.io/component: telegram-polling +{{- end }} diff --git a/helm/oncall/templates/telegram-polling/deployment.yaml b/helm/oncall/templates/telegram-polling/deployment.yaml new file mode 100644 index 00000000..081599af --- /dev/null +++ b/helm/oncall/templates/telegram-polling/deployment.yaml @@ -0,0 +1,44 @@ +{{- if .Values.telegramPolling.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "oncall.telegramPolling.fullname" . }} + labels: + {{- include "oncall.telegramPolling.labels" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + {{- include "oncall.telegramPolling.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "oncall.telegramPolling.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "oncall.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + {{- include "oncall.initContainer" . | nindent 8 }} + containers: + - name: telegram-polling + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ['sh', '-c', 'python manage.py start_telegram_polling'] + env: + {{- include "snippet.oncall.env" . | nindent 12 }} + {{- include "snippet.oncall.telegram.env" . | nindent 12 }} + {{- include "snippet.db.env" . | nindent 12 }} + {{- include "snippet.broker.env" . | nindent 12 }} + {{- include "oncall.extraEnvs" . | nindent 12 }} + {{- with .Values.telegramPolling.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} +{{- end -}} diff --git a/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap b/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap new file mode 100644 index 00000000..18e4e440 --- /dev/null +++ b/helm/oncall/tests/__snapshot__/telegram_polling_deployment_test.yaml.snap @@ -0,0 +1,73 @@ +telegramPolling.enabled=true -> should create telegram polling deployment: + 1: | + - command: + - sh + - -c + - python manage.py start_telegram_polling + env: + - name: BASE_URL + value: https://example.com + - name: SECRET_KEY + valueFrom: + secretKeyRef: + key: SECRET_KEY + name: oncall + - name: MIRAGE_SECRET_KEY + valueFrom: + secretKeyRef: + key: MIRAGE_SECRET_KEY + name: oncall + - name: MIRAGE_CIPHER_IV + value: 1234567890abcdef + - name: DJANGO_SETTINGS_MODULE + value: settings.helm + - name: AMIXR_DJANGO_ADMIN_PATH + value: admin + - name: OSS + value: "True" + - name: UWSGI_LISTEN + value: "1024" + - name: BROKER_TYPE + value: rabbitmq + - name: GRAFANA_API_URL + value: http://oncall-grafana + - name: FEATURE_TELEGRAM_INTEGRATION_ENABLED + value: "True" + - name: FEATURE_TELEGRAM_LONG_POLLING_ENABLED + value: "True" + - name: TELEGRAM_WEBHOOK_HOST + value: https://example.com + - name: TELEGRAM_TOKEN + value: "" + - name: MYSQL_HOST + value: oncall-mariadb + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_DB_NAME + value: oncall + - name: MYSQL_USER + value: root + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + key: mariadb-root-password + name: oncall-mariadb + - name: RABBITMQ_USERNAME + value: user + - name: RABBITMQ_PASSWORD + valueFrom: + secretKeyRef: + key: rabbitmq-password + name: oncall-rabbitmq + - name: RABBITMQ_HOST + value: oncall-rabbitmq + - name: RABBITMQ_PORT + value: "5672" + - name: RABBITMQ_PROTOCOL + value: amqp + - name: RABBITMQ_VHOST + value: "" + image: grafana/oncall:v1.3.20 + imagePullPolicy: Always + name: telegram-polling + securityContext: {} diff --git a/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap b/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap index 1b6ff0ef..8a4586aa 100644 --- a/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap +++ b/helm/oncall/tests/__snapshot__/wait_for_db_test.yaml.snap @@ -141,6 +141,77 @@ database.type=mysql -> should create initContainer for MySQL database (default): cpu: 100m memory: 128Mi securityContext: {} + 3: | + - command: + - sh + - -c + - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done + env: + - name: BASE_URL + value: https://example.com + - name: SECRET_KEY + valueFrom: + secretKeyRef: + key: SECRET_KEY + name: oncall + - name: MIRAGE_SECRET_KEY + valueFrom: + secretKeyRef: + key: MIRAGE_SECRET_KEY + name: oncall + - name: MIRAGE_CIPHER_IV + value: 1234567890abcdef + - name: DJANGO_SETTINGS_MODULE + value: settings.helm + - name: AMIXR_DJANGO_ADMIN_PATH + value: admin + - name: OSS + value: "True" + - name: UWSGI_LISTEN + value: "1024" + - name: BROKER_TYPE + value: rabbitmq + - name: GRAFANA_API_URL + value: http://oncall-grafana + - name: MYSQL_HOST + value: oncall-mariadb + - name: MYSQL_PORT + value: "3306" + - name: MYSQL_DB_NAME + value: oncall + - name: MYSQL_USER + value: root + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + key: mariadb-root-password + name: oncall-mariadb + - name: RABBITMQ_USERNAME + value: user + - name: RABBITMQ_PASSWORD + valueFrom: + secretKeyRef: + key: rabbitmq-password + name: oncall-rabbitmq + - name: RABBITMQ_HOST + value: oncall-rabbitmq + - name: RABBITMQ_PORT + value: "5672" + - name: RABBITMQ_PROTOCOL + value: amqp + - name: RABBITMQ_VHOST + value: "" + image: grafana/oncall:v1.2.36 + imagePullPolicy: Always + name: wait-for-db + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: {} database.type=postgresql -> should create initContainer for PostgreSQL database: 1: | - command: @@ -288,3 +359,76 @@ database.type=postgresql -> should create initContainer for PostgreSQL database: cpu: 100m memory: 128Mi securityContext: {} + 3: | + - command: + - sh + - -c + - until (python manage.py migrate --check); do echo Waiting for database migrations; sleep 2; done + env: + - name: BASE_URL + value: https://example.com + - name: SECRET_KEY + valueFrom: + secretKeyRef: + key: SECRET_KEY + name: oncall + - name: MIRAGE_SECRET_KEY + valueFrom: + secretKeyRef: + key: MIRAGE_SECRET_KEY + name: oncall + - name: MIRAGE_CIPHER_IV + value: 1234567890abcdef + - name: DJANGO_SETTINGS_MODULE + value: settings.helm + - name: AMIXR_DJANGO_ADMIN_PATH + value: admin + - name: OSS + value: "True" + - name: UWSGI_LISTEN + value: "1024" + - name: BROKER_TYPE + value: rabbitmq + - name: GRAFANA_API_URL + value: http://oncall-grafana + - name: DATABASE_TYPE + value: postgresql + - name: DATABASE_HOST + value: some-postgresql-host + - name: DATABASE_PORT + value: "5432" + - name: DATABASE_NAME + value: oncall + - name: DATABASE_USER + value: postgres + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + key: postgres-password + name: oncall-postgresql-external + - name: RABBITMQ_USERNAME + value: user + - name: RABBITMQ_PASSWORD + valueFrom: + secretKeyRef: + key: rabbitmq-password + name: oncall-rabbitmq + - name: RABBITMQ_HOST + value: oncall-rabbitmq + - name: RABBITMQ_PORT + value: "5672" + - name: RABBITMQ_PROTOCOL + value: amqp + - name: RABBITMQ_VHOST + value: "" + image: grafana/oncall:v1.2.36 + imagePullPolicy: Always + name: wait-for-db + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: {} diff --git a/helm/oncall/tests/extra_env_test.yaml b/helm/oncall/tests/extra_env_test.yaml index 3ea4fa4e..8b502e27 100644 --- a/helm/oncall/tests/extra_env_test.yaml +++ b/helm/oncall/tests/extra_env_test.yaml @@ -3,11 +3,13 @@ templates: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: env=[] -> should support old syntax set: + telegramPolling.enabled: true env: - name: SOME_VAR value: some_value @@ -20,6 +22,7 @@ tests: - it: env=map[] -> should set multiple envs set: + telegramPolling.enabled: true env: SOME_VAR: some_value another_var: "another_value" @@ -39,7 +42,9 @@ tests: templates: - engine/deployment.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true env: - name: SOME_VAR value: some_value @@ -54,7 +59,9 @@ tests: templates: - engine/deployment.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true env: SOME_VAR: some_value another_var: "another_value" @@ -74,7 +81,9 @@ tests: templates: - engine/deployment.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: true env: @@ -96,7 +105,9 @@ tests: templates: - engine/deployment.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: true env: diff --git a/helm/oncall/tests/image_deployments_test.yaml b/helm/oncall/tests/image_deployments_test.yaml index d171aae7..c7a1ae09 100644 --- a/helm/oncall/tests/image_deployments_test.yaml +++ b/helm/oncall/tests/image_deployments_test.yaml @@ -3,12 +3,15 @@ templates: - celery/deployment.yaml - engine/deployment.yaml - engine/job-migrate.yaml + - telegram-polling/deployment.yaml release: name: oncall chart: appVersion: 1.2.36 tests: - it: image={} -> should use default image tag + set: + telegramPolling.enabled: true asserts: - equal: path: spec.template.spec.containers[0].image @@ -19,6 +22,7 @@ tests: - it: image.repository and image.tag -> should use custom image set: + telegramPolling.enabled: true image: repository: custom-oncall tag: 1.2.36-custom diff --git a/helm/oncall/tests/image_pull_secrets_test.yaml b/helm/oncall/tests/image_pull_secrets_test.yaml index 8cf22388..2bbe5b40 100644 --- a/helm/oncall/tests/image_pull_secrets_test.yaml +++ b/helm/oncall/tests/image_pull_secrets_test.yaml @@ -3,12 +3,14 @@ templates: - celery/deployment.yaml - engine/deployment.yaml - engine/job-migrate.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: imagePullSecrets=[] -> should not create spec.template.spec.imagePullSecrets set: imagePullSecrets: [] + telegramPolling.enabled: true asserts: - notExists: path: spec.template.spec.imagePullSecrets @@ -17,6 +19,7 @@ tests: set: imagePullSecrets: - name: regcred + telegramPolling.enabled: true asserts: - contains: path: spec.template.spec.imagePullSecrets diff --git a/helm/oncall/tests/mysql_env_test.yaml b/helm/oncall/tests/mysql_env_test.yaml index 845842bf..cc993045 100644 --- a/helm/oncall/tests/mysql_env_test.yaml +++ b/helm/oncall/tests/mysql_env_test.yaml @@ -3,11 +3,13 @@ templates: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: mariadb.enabled=false -> external MySQL default settings set: + telegramPolling.enabled: true mariadb.enabled: false asserts: - contains: @@ -38,6 +40,7 @@ tests: - it: externalMysql -> use external MySQL custom settings set: + telegramPolling.enabled: true mariadb.enabled: false externalMysql: host: test-host @@ -67,6 +70,8 @@ tests: value: test-host - it: mariadb.enabled=true -> internal MySQL default settings + set: + telegramPolling.enabled: true asserts: - contains: path: spec.template.spec.containers[0].env @@ -91,6 +96,7 @@ tests: - it: mariadb.auth -> internal MySQL custom settings set: + telegramPolling.enabled: true mariadb: auth: database: grafana_oncall diff --git a/helm/oncall/tests/mysql_password_env_test.yaml b/helm/oncall/tests/mysql_password_env_test.yaml index b57d815f..c6a6987e 100644 --- a/helm/oncall/tests/mysql_password_env_test.yaml +++ b/helm/oncall/tests/mysql_password_env_test.yaml @@ -5,6 +5,7 @@ templates: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml - secrets.yaml tests: - it: secrets -> should fail if externalMysql.password not set @@ -20,7 +21,9 @@ tests: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true mariadb.enabled: false externalMysql: user: user123 diff --git a/helm/oncall/tests/postgres_env_test.yaml b/helm/oncall/tests/postgres_env_test.yaml index fccdb4f2..c46caaf9 100644 --- a/helm/oncall/tests/postgres_env_test.yaml +++ b/helm/oncall/tests/postgres_env_test.yaml @@ -3,11 +3,13 @@ templates: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: postgresql.enabled=false -> external PostgreSQL default settings set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: false externalPostgresql.host: custom-postgres-host @@ -40,6 +42,7 @@ tests: - it: externalPostgresql -> should use external PostgreSQL custom settings set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: false externalPostgresql: @@ -76,6 +79,7 @@ tests: - it: postgresql.enabled=true -> internal PostgreSQL default settings set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: true asserts: @@ -107,6 +111,7 @@ tests: - it: postgresql.auth -> should use internal PostgreSQL custom settings set: + telegramPolling.enabled: true database.type: postgresql postgresql: enabled: true diff --git a/helm/oncall/tests/postgres_password_env_test.yaml b/helm/oncall/tests/postgres_password_env_test.yaml index e5b9f0f5..56ea50dc 100644 --- a/helm/oncall/tests/postgres_password_env_test.yaml +++ b/helm/oncall/tests/postgres_password_env_test.yaml @@ -5,6 +5,7 @@ templates: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml - secrets.yaml tests: - it: secrets -> should fail if externalPostgresql.password not set @@ -23,7 +24,9 @@ tests: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: false externalPostgresql: @@ -55,7 +58,9 @@ tests: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: false externalPostgresql: @@ -77,7 +82,9 @@ tests: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true database.type: postgresql postgresql.enabled: false externalPostgresql: @@ -99,7 +106,9 @@ tests: - engine/deployment.yaml - engine/job-migrate.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml set: + telegramPolling.enabled: true database.type: postgresql postgresql: enabled: true diff --git a/helm/oncall/tests/security_context_deployments_test.yaml b/helm/oncall/tests/security_context_deployments_test.yaml index 8f0f4055..5514c24c 100644 --- a/helm/oncall/tests/security_context_deployments_test.yaml +++ b/helm/oncall/tests/security_context_deployments_test.yaml @@ -3,11 +3,13 @@ templates: - celery/deployment.yaml - engine/deployment.yaml - engine/job-migrate.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: podSecurityContext={} -> spec.template.spec.securityContext is empty (default) set: + telegramPolling.enabled: true asserts: - isNullOrEmpty: path: spec.template.spec.securityContext @@ -16,6 +18,7 @@ tests: - it: podSecurityContext.runAsNonRoot=true -> should fill securityContext set: + telegramPolling.enabled: true podSecurityContext: runAsNonRoot: true runAsUser: 1000 @@ -28,6 +31,7 @@ tests: - it: securityContext.runAsNonRoot=true -> should fill securityContext for container set: + telegramPolling.enabled: true securityContext: runAsNonRoot: true runAsUser: 1000 diff --git a/helm/oncall/tests/service_account_deployments_test.yaml b/helm/oncall/tests/service_account_deployments_test.yaml index 0c1d7358..89bf134d 100644 --- a/helm/oncall/tests/service_account_deployments_test.yaml +++ b/helm/oncall/tests/service_account_deployments_test.yaml @@ -3,10 +3,13 @@ templates: - celery/deployment.yaml - engine/deployment.yaml - engine/job-migrate.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: serviceAccount.create=true -> should use created serviceAccount for deployments (default) + set: + telegramPolling.enabled: true asserts: - equal: path: spec.template.spec.serviceAccountName @@ -14,6 +17,7 @@ tests: - it: serviceAccount.create=false -> should use default serviceAccount for deployments set: + telegramPolling.enabled: true serviceAccount.create: false asserts: - equal: @@ -22,6 +26,7 @@ tests: - it: serviceAccount.name=custom -> should use created custom serviceAccount for deployments set: + telegramPolling.enabled: true serviceAccount.name: custom asserts: - equal: diff --git a/helm/oncall/tests/telegram_env_test.yaml b/helm/oncall/tests/telegram_env_test.yaml index 65e94cd3..288d5ee6 100644 --- a/helm/oncall/tests/telegram_env_test.yaml +++ b/helm/oncall/tests/telegram_env_test.yaml @@ -2,10 +2,14 @@ suite: test telegram envs for deployments templates: - engine/deployment.yaml - celery/deployment.yaml + - telegram-polling/deployment.yaml release: name: oncall tests: - it: oncall.telegram.enabled=false -> Telegram integration disabled (default) + templates: + - engine/deployment.yaml + - celery/deployment.yaml asserts: - contains: path: spec.template.spec.containers[0].env @@ -14,6 +18,9 @@ tests: value: "False" - it: oncall.telegram.enabled=true -> should enable Telegram integration + templates: + - engine/deployment.yaml + - celery/deployment.yaml set: oncall.telegram: enabled: true @@ -37,6 +44,9 @@ tests: value: "abcd:123" - it: oncall.telegram.existingSecret=some-secret -> should prefer existing secret over oncall.telegram.token + templates: + - engine/deployment.yaml + - celery/deployment.yaml set: oncall.telegram: enabled: true @@ -52,3 +62,18 @@ tests: secretKeyRef: name: some-secret key: token + + - it: telegramPolling.enabled=true -> should enable oncall.telegram.enabled too + set: + telegramPolling.enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: FEATURE_TELEGRAM_INTEGRATION_ENABLED + value: "True" + - contains: + path: spec.template.spec.containers[0].env + content: + name: FEATURE_TELEGRAM_LONG_POLLING_ENABLED + value: "True" diff --git a/helm/oncall/tests/telegram_polling_deployment_test.yaml b/helm/oncall/tests/telegram_polling_deployment_test.yaml new file mode 100644 index 00000000..96fb8d01 --- /dev/null +++ b/helm/oncall/tests/telegram_polling_deployment_test.yaml @@ -0,0 +1,73 @@ +suite: test telegram polling deployment +templates: + - telegram-polling/deployment.yaml +release: + name: oncall +tests: + - it: telegramPolling.enabled=false -> should not create deployment (default) + asserts: + - hasDocuments: + count: 0 + + - it: telegramPolling.enabled=true -> should create telegram polling deployment + set: + telegramPolling.enabled: true + asserts: + - containsDocument: + kind: Deployment + apiVersion: apps/v1 + name: oncall-telegram-polling + - isSubset: + path: metadata.labels + content: + app.kubernetes.io/component: telegram-polling + app.kubernetes.io/instance: oncall + app.kubernetes.io/name: oncall + - isSubset: + path: spec.selector.matchLabels + content: + app.kubernetes.io/component: telegram-polling + app.kubernetes.io/instance: oncall + app.kubernetes.io/name: oncall + - isSubset: + path: spec.template.metadata.labels + content: + app.kubernetes.io/component: telegram-polling + app.kubernetes.io/instance: oncall + app.kubernetes.io/name: oncall + # Should contain only one replica to avoid Conflict while polling Telegram updates + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.template.spec.serviceAccountName + value: oncall + - contains: + path: spec.template.spec.initContainers + content: + name: wait-for-db + any: true + - matchSnapshot: + path: spec.template.spec.containers + + - it: telegramPolling.resources -> should specify resources + set: + telegramPolling: + enabled: true + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + asserts: + - equal: + path: spec.template.spec.containers[0].resources + value: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi diff --git a/helm/oncall/tests/wait_for_db_test.yaml b/helm/oncall/tests/wait_for_db_test.yaml index 6860389a..b1fed706 100644 --- a/helm/oncall/tests/wait_for_db_test.yaml +++ b/helm/oncall/tests/wait_for_db_test.yaml @@ -2,6 +2,7 @@ suite: test init container wait-for-db in deployments templates: - celery/deployment.yaml - engine/deployment.yaml + - telegram-polling/deployment.yaml release: name: oncall chart: @@ -16,6 +17,8 @@ tests: requests: cpu: 100m memory: 128Mi + telegramPolling.enabled: true + database.type: mysql asserts: - contains: path: spec.template.spec.initContainers @@ -32,6 +35,7 @@ tests: - it: database.type=postgresql -> should create initContainer for PostgreSQL database set: + telegramPolling.enabled: true database.type: postgresql externalPostgresql.host: some-postgresql-host init.resources: diff --git a/helm/oncall/values.yaml b/helm/oncall/values.yaml index d6f71ac7..73f07b4d 100644 --- a/helm/oncall/values.yaml +++ b/helm/oncall/values.yaml @@ -134,6 +134,17 @@ celery: # - --port=5432 # - example:europe-west3:grafana-oncall-db +# Telegram polling pod configuration +telegramPolling: + enabled: false + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + oncall: # this is intended to be used for local development. In short, it will mount the ./engine dir into # any backend related containers, to allow hot-reloading + also run the containers with slightly modified