Merge branch 'dev' of https://github.com/grafana/oncall into dev
This commit is contained in:
commit
385738dd48
28 changed files with 232 additions and 238 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -3,7 +3,7 @@
|
|||
*.pyc
|
||||
venv
|
||||
.env
|
||||
.env-hobby
|
||||
.env_hobby
|
||||
.vscode
|
||||
dump.rdb
|
||||
.idea
|
||||
|
|
|
|||
|
|
@ -53,9 +53,6 @@ export $(grep -v '^#' .env | xargs -0)
|
|||
# Hint: there is a known issue with uwsgi. It's not used in the local dev environment. Feel free to comment it in `engine/requirements.txt`.
|
||||
cd engine && pip install -r requirements.txt
|
||||
|
||||
# Create folder for database
|
||||
mkdir sqlite_data
|
||||
|
||||
# Migrate the DB:
|
||||
python manage.py migrate
|
||||
|
||||
|
|
@ -107,7 +104,7 @@ python manage.py issue_invite_for_the_frontend --override
|
|||
OnCall API URL:
|
||||
http://host.docker.internal:8000
|
||||
|
||||
OnCall Invitation Token (Single use token to connect Grafana instance):
|
||||
Invitation Token (Single use token to connect Grafana instance):
|
||||
Response from the invite generator command (check above)
|
||||
|
||||
Grafana URL (URL OnCall will use to talk to Grafana instance):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<img width="400px" src="docs/img/logo.png">
|
||||
|
||||
Developer-friendly, incident response management with brilliant Slack integration.
|
||||
Developer-friendly, incident response with brilliant Slack integration.
|
||||
|
||||
<img width="60%" src="screenshot.png">
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ services:
|
|||
ports:
|
||||
- 3306:3306
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: local_dev_pwd
|
||||
MYSQL_ROOT_PASSWORD: empty
|
||||
MYSQL_DATABASE: oncall_local_dev
|
||||
healthcheck:
|
||||
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
|
||||
|
|
@ -42,13 +42,13 @@ services:
|
|||
mysql-to-create-grafana-db:
|
||||
image: mariadb:10.2
|
||||
platform: linux/x86_64
|
||||
command: bash -c "mysql -h mysql -uroot -plocal_dev_pwd -e 'CREATE DATABASE IF NOT EXISTS grafana CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'"
|
||||
command: bash -c "mysql -h mysql -uroot -pempty -e 'CREATE DATABASE IF NOT EXISTS grafana CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'"
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
|
||||
grafana:
|
||||
image: "grafana/grafana:8.5.5"
|
||||
image: "grafana/grafana:9.0.0-beta3"
|
||||
restart: always
|
||||
mem_limit: 500m
|
||||
cpus: 0.5
|
||||
|
|
@ -56,7 +56,7 @@ services:
|
|||
GF_DATABASE_TYPE: mysql
|
||||
GF_DATABASE_HOST: mysql
|
||||
GF_DATABASE_USER: root
|
||||
GF_DATABASE_PASSWORD: local_dev_pwd
|
||||
GF_DATABASE_PASSWORD: empty
|
||||
GF_SECURITY_ADMIN_USER: oncall
|
||||
GF_SECURITY_ADMIN_PASSWORD: oncall
|
||||
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ services:
|
|||
- with_grafana
|
||||
|
||||
grafana:
|
||||
image: "grafana/grafana:8.3.2"
|
||||
image: "grafana/grafana:9.0.0-beta3"
|
||||
mem_limit: 500m
|
||||
ports:
|
||||
- 3000:3000
|
||||
|
|
|
|||
|
|
@ -113,20 +113,7 @@ class ChannelFilter(OrderedModel):
|
|||
return satisfied_filter
|
||||
|
||||
def is_satisfying(self, raw_request_data, title, message=None):
|
||||
AlertReceiveChannel = apps.get_model("alerts", "AlertReceiveChannel")
|
||||
|
||||
return (
|
||||
self.is_default
|
||||
or self.check_filter(json.dumps(raw_request_data))
|
||||
or self.check_filter(str(title))
|
||||
or
|
||||
# Special case for Amazon SNS
|
||||
(
|
||||
self.check_filter(str(message))
|
||||
if self.alert_receive_channel.integration == AlertReceiveChannel.INTEGRATION_AMAZON_SNS
|
||||
else False
|
||||
)
|
||||
)
|
||||
return self.is_default or self.check_filter(json.dumps(raw_request_data)) or self.check_filter(str(title))
|
||||
|
||||
def check_filter(self, value):
|
||||
return re.search(self.filtering_term, value)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
from apps.alerts.incident_appearance.templaters import AlertSlackTemplater
|
||||
from apps.alerts.models import AlertGroup
|
||||
from apps.integrations.metadata.configuration import grafana
|
||||
from config_integrations import grafana
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ from apps.alerts.incident_appearance.templaters import (
|
|||
AlertWebTemplater,
|
||||
)
|
||||
from apps.alerts.models import Alert, AlertReceiveChannel
|
||||
from apps.integrations.metadata.configuration import grafana
|
||||
from common.jinja_templater import jinja_template_env
|
||||
from common.utils import getattrd
|
||||
from config_integrations import grafana
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
# Main
|
||||
enabled = True
|
||||
title = "Amazon SNS"
|
||||
slug = "amazon_sns"
|
||||
short_description = None
|
||||
is_displayed_on_web = True
|
||||
description = None
|
||||
is_featured = False
|
||||
is_able_to_autoresolve = True
|
||||
is_demo_alert_enabled = True
|
||||
|
||||
description = None
|
||||
|
||||
# Default templates
|
||||
slack_title = """\
|
||||
{% if payload|length == 0 -%}
|
||||
{% set title = payload.get("AlarmName", "Alert") %}
|
||||
{%- else -%}
|
||||
{% set title = "Alert" %}
|
||||
{%- endif %}
|
||||
|
||||
*<{{ grafana_oncall_link }}|#{{ grafana_oncall_incident_id }} {{ title }}>* via {{ integration_name }}
|
||||
{% if source_link %}
|
||||
(*<{{ source_link }}|source>*)
|
||||
{%- endif %}"""
|
||||
|
||||
slack_message = """\
|
||||
{% if payload|length == 1 and "message" in payload -%}
|
||||
{{ payload.get("message", "Non-JSON payload received. Please make sure you publish monitoring Alarms to SNS, not logs: https://docs.amixr.io/#/integrations/amazon_sns") }}
|
||||
{%- else -%}
|
||||
*State* {{ payload.get("NewStateValue", "NO") }}
|
||||
Region: {{ payload.get("Region", "Undefined") }}
|
||||
_Description_: {{ payload.get("AlarmDescription", "Undefined") }}
|
||||
{%- endif %}
|
||||
"""
|
||||
|
||||
slack_image_url = None
|
||||
|
||||
web_title = """\
|
||||
{% if payload|length == 0 -%}
|
||||
{{ payload.get("AlarmName", "Alert")}}
|
||||
{%- else -%}
|
||||
Alert
|
||||
{%- endif %}"""
|
||||
|
||||
web_message = """\
|
||||
{% if payload|length == 1 and "message" in payload -%}
|
||||
{{ payload.get("message", "Non-JSON payload received. Please make sure you publish monitoring Alarms to SNS, not logs: https://docs.amixr.io/#/integrations/amazon_sns") }}
|
||||
{%- else -%}
|
||||
**State** {{ payload.get("NewStateValue", "NO") }}
|
||||
Region: {{ payload.get("Region", "Undefined") }}
|
||||
*Description*: {{ payload.get("AlarmDescription", "Undefined") }}
|
||||
{%- endif %}
|
||||
"""
|
||||
|
||||
web_image_url = slack_image_url
|
||||
|
||||
sms_title = web_title
|
||||
|
||||
phone_call_title = web_title
|
||||
|
||||
email_title = web_title
|
||||
|
||||
email_message = "{{ payload|tojson_pretty }}"
|
||||
|
||||
telegram_title = sms_title
|
||||
|
||||
telegram_message = """\
|
||||
{% if payload|length == 1 and "message" in payload -%}
|
||||
{{ payload.get("message", "Non-JSON payload received. Please make sure you publish monitoring Alarms to SNS, not logs: https://docs.amixr.io/#/integrations/amazon_sns") }}
|
||||
{%- else -%}
|
||||
<b>State</b> {{ payload.get("NewStateValue", "NO") }}
|
||||
Region: {{ payload.get("Region", "Undefined") }}
|
||||
<i>Description</i>: {{ payload.get("AlarmDescription", "Undefined") }}
|
||||
{%- endif %}
|
||||
"""
|
||||
|
||||
telegram_image_url = slack_image_url
|
||||
|
||||
source_link = """\
|
||||
{% if payload|length == 0 -%}
|
||||
{% if payload.get("Trigger", {}).get("Namespace") == "AWS/ElasticBeanstalk" -%}
|
||||
https://console.aws.amazon.com/elasticbeanstalk/home?region={{ payload.get("TopicArn").split(":")[3] }}
|
||||
{%- else -%}
|
||||
https://console.aws.amazon.com/cloudwatch//home?region={{ payload.get("TopicArn").split(":")[3] }}
|
||||
{%- endif %}
|
||||
{%- endif %}"""
|
||||
|
||||
grouping_id = web_title
|
||||
|
||||
resolve_condition = """\
|
||||
{{ payload.get("NewStateValue", "") == "OK" }}
|
||||
"""
|
||||
|
||||
acknowledge_condition = None
|
||||
|
||||
group_verbose_name = web_title
|
||||
|
||||
example_payload = {"foo": "bar"}
|
||||
|
|
@ -56,7 +56,11 @@ class OpenAlertAppearanceDialogStep(
|
|||
raw_request_data = json.dumps(alert_group.alerts.first().raw_request_data, sort_keys=True, indent=4)
|
||||
|
||||
# This is a special case for amazon sns notifications in str format CHEKED
|
||||
if alert_group.channel.integration == AlertReceiveChannel.INTEGRATION_AMAZON_SNS and raw_request_data == "{}":
|
||||
if (
|
||||
AlertReceiveChannel.INTEGRATION_AMAZON_SNS is not None
|
||||
and alert_group.channel.integration == AlertReceiveChannel.INTEGRATION_AMAZON_SNS
|
||||
and raw_request_data == "{}"
|
||||
):
|
||||
raw_request_data = alert_group.alerts.first().message
|
||||
|
||||
raw_request_data_chunks = [
|
||||
|
|
|
|||
66
engine/config_integrations/elastalert.py
Normal file
66
engine/config_integrations/elastalert.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Main
|
||||
enabled = True
|
||||
title = "Elastalert"
|
||||
slug = "elastalert"
|
||||
short_description = "Elastic"
|
||||
is_displayed_on_web = True
|
||||
description = None
|
||||
is_featured = False
|
||||
is_able_to_autoresolve = True
|
||||
is_demo_alert_enabled = True
|
||||
|
||||
description = None
|
||||
|
||||
# Default templates
|
||||
slack_title = """\
|
||||
*<{{ grafana_oncall_link }}|#{{ grafana_oncall_incident_id }} Incident>* via {{ integration_name }}
|
||||
{% if source_link %}
|
||||
(*<{{ source_link }}|source>*)
|
||||
{%- endif %}"""
|
||||
|
||||
slack_message = "```{{ payload|tojson_pretty }}```"
|
||||
|
||||
slack_image_url = None
|
||||
|
||||
web_title = "Incident"
|
||||
|
||||
web_message = """\
|
||||
```
|
||||
{{ payload|tojson_pretty }}
|
||||
```
|
||||
"""
|
||||
|
||||
web_image_url = slack_image_url
|
||||
|
||||
sms_title = web_title
|
||||
|
||||
phone_call_title = sms_title
|
||||
|
||||
email_title = web_title
|
||||
|
||||
email_message = "{{ payload|tojson_pretty }}"
|
||||
|
||||
telegram_title = sms_title
|
||||
|
||||
telegram_message = "<code>{{ payload|tojson_pretty }}</code>"
|
||||
|
||||
telegram_image_url = slack_image_url
|
||||
|
||||
source_link = None
|
||||
|
||||
grouping_id = '{{ payload.get("alert_uid", "")}}'
|
||||
|
||||
resolve_condition = """\
|
||||
{%- if "is_amixr_heartbeat_restored" in payload -%}
|
||||
{# We don't know the payload format from your integration. #}
|
||||
{# The heartbeat alerts will go here so we check for our own key #}
|
||||
{{ payload["is_amixr_heartbeat_restored"] }}
|
||||
{%- else -%}
|
||||
{{ payload.get("state", "").upper() == "OK" }}'
|
||||
{%- endif %}"""
|
||||
|
||||
acknowledge_condition = None
|
||||
|
||||
group_verbose_name = "Incident"
|
||||
|
||||
example_payload = {"message": "This alert was sent by user for the demonstration purposes"}
|
||||
65
engine/config_integrations/kapacitor.py
Normal file
65
engine/config_integrations/kapacitor.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Main
|
||||
enabled = True
|
||||
title = "Kapacitor"
|
||||
slug = "kapacitor"
|
||||
short_description = "InfluxDB"
|
||||
description = None
|
||||
is_displayed_on_web = True
|
||||
is_featured = False
|
||||
is_able_to_autoresolve = True
|
||||
is_demo_alert_enabled = True
|
||||
|
||||
description = None
|
||||
|
||||
# Default templates
|
||||
slack_title = """\
|
||||
*<{{ grafana_oncall_link }}|#{{ grafana_oncall_incident_id }} {{ payload.get("id", "Title undefined (check Slack Title Template)") }}>* via {{ integration_name }}
|
||||
{% if source_link %}
|
||||
(*<{{ source_link }}|source>*)
|
||||
{%- endif %}"""
|
||||
|
||||
slack_message = """\
|
||||
```{{ payload|tojson_pretty }}```
|
||||
"""
|
||||
|
||||
slack_image_url = None
|
||||
|
||||
web_title = '{{ payload.get("id", "Title undefined (check Web Title Template)") }}'
|
||||
|
||||
web_message = """\
|
||||
```
|
||||
{{ payload|tojson_pretty }}
|
||||
```
|
||||
"""
|
||||
|
||||
web_image_url = slack_image_url
|
||||
|
||||
sms_title = web_title
|
||||
|
||||
phone_call_title = web_title
|
||||
|
||||
email_title = web_title
|
||||
|
||||
email_message = slack_message
|
||||
|
||||
telegram_title = sms_title
|
||||
|
||||
telegram_message = "<code>{{ payload|tojson_pretty }}</code>"
|
||||
|
||||
telegram_image_url = slack_image_url
|
||||
|
||||
source_link = None
|
||||
|
||||
grouping_id = '{{ payload.get("id", "") }}'
|
||||
|
||||
resolve_condition = '{{ payload.get("level", "").startswith("OK") }}'
|
||||
|
||||
acknowledge_condition = None
|
||||
|
||||
group_verbose_name = '{{ payload.get("id", "") }}'
|
||||
|
||||
example_payload = {
|
||||
"id": "TestAlert",
|
||||
"message": "This alert was sent by user for the demonstration purposes",
|
||||
"data": "{foo: bar}",
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
export DJANGO_SETTINGS_MODULE=settings.all_in_one
|
||||
|
||||
generate_value_if_not_exist ()
|
||||
{
|
||||
if [ ! -f /etc/app/secret_data/$1 ]; then
|
||||
touch /etc/app/secret_data/$1
|
||||
base64 /dev/urandom | head -c $2 > /etc/app/secret_data/$1
|
||||
fi
|
||||
export $1=$(cat /etc/app/secret_data/$1)
|
||||
}
|
||||
|
||||
generate_value_if_not_exist SECRET_KEY 75
|
||||
|
||||
generate_value_if_not_exist MIRAGE_SECRET_KEY 75
|
||||
generate_value_if_not_exist MIRAGE_CIPHER_IV 16
|
||||
|
||||
export BASE_URL=http://localhost:8000
|
||||
|
||||
echo "Starting redis in the background"
|
||||
# Redis will dump the changes to the volume every 60 seconds if at least 1 key changed
|
||||
redis-server --daemonize yes --save 60 1 --dir /etc/app/redis_data/
|
||||
echo "Running migrations"
|
||||
python manage.py migrate
|
||||
|
||||
echo "Start celery"
|
||||
python manage.py start_celery &
|
||||
|
||||
# Postponing token issuing to make sure it's the last record in the console.
|
||||
bash -c 'sleep 10; python manage.py issue_invite_for_the_frontend --override' &
|
||||
|
||||
echo "Starting server"
|
||||
python manage.py runserver 0.0.0.0:8000 --noreload
|
||||
|
|
@ -428,15 +428,16 @@ FEATURE_EXTRA_MESSAGING_BACKENDS_ENABLED = getenv_boolean("FEATURE_EXTRA_MESSAGI
|
|||
EXTRA_MESSAGING_BACKENDS = []
|
||||
|
||||
INSTALLED_ONCALL_INTEGRATIONS = [
|
||||
"apps.integrations.metadata.configuration.alertmanager",
|
||||
"apps.integrations.metadata.configuration.grafana",
|
||||
"apps.integrations.metadata.configuration.grafana_alerting",
|
||||
"apps.integrations.metadata.configuration.formatted_webhook",
|
||||
"apps.integrations.metadata.configuration.webhook",
|
||||
"apps.integrations.metadata.configuration.amazon_sns",
|
||||
"apps.integrations.metadata.configuration.heartbeat",
|
||||
"apps.integrations.metadata.configuration.inbound_email",
|
||||
"apps.integrations.metadata.configuration.maintenance",
|
||||
"apps.integrations.metadata.configuration.manual",
|
||||
"apps.integrations.metadata.configuration.slack_channel",
|
||||
"config_integrations.alertmanager",
|
||||
"config_integrations.grafana",
|
||||
"config_integrations.grafana_alerting",
|
||||
"config_integrations.formatted_webhook",
|
||||
"config_integrations.webhook",
|
||||
"config_integrations.kapacitor",
|
||||
"config_integrations.elastalert",
|
||||
"config_integrations.heartbeat",
|
||||
"config_integrations.inbound_email",
|
||||
"config_integrations.maintenance",
|
||||
"config_integrations.manual",
|
||||
"config_integrations.slack_channel",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
# Workaround to use pymysql instead of mysqlclient
|
||||
import pymysql
|
||||
|
||||
from .base import * # noqa
|
||||
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY", "osMsNM0PqlRHBlUvqmeJ7+ldU3IUETCrY9TrmiViaSmInBHolr1WUlS0OFS4AHrnnkp1vp9S9z1")
|
||||
|
|
@ -10,14 +13,26 @@ MIRAGE_SECRET_KEY = os.environ.get(
|
|||
)
|
||||
MIRAGE_CIPHER_IV = os.environ.get("MIRAGE_CIPHER_IV", "tZZa+60zTZO2NRcS")
|
||||
|
||||
# Primary database must have the name "default"
|
||||
pymysql.install_as_MySQLdb()
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": os.path.join(BASE_DIR, "sqlite_data/db.sqlite3"), # noqa
|
||||
"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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
os.environ.setdefault("OSS", "True")
|
||||
INSTALLED_APPS += ["apps.oss_installation"] # noqa
|
||||
|
||||
TESTING = "pytest" in sys.modules or "unittest" in sys.modules
|
||||
|
||||
READONLY_DATABASES = {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
.delete_configuration_button {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.command-line {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-block {
|
||||
margin-bottom: 24px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import cn from 'classnames/bind';
|
|||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { OnCallAppSettings } from 'types';
|
||||
|
||||
import Block from 'components/GBlock/Block';
|
||||
import Text from 'components/Text/Text';
|
||||
import WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import logo from 'img/logo.svg';
|
||||
|
|
@ -34,23 +35,20 @@ const cx = cn.bind(styles);
|
|||
interface Props extends PluginConfigPageProps<AppPluginMeta<OnCallAppSettings>> {}
|
||||
|
||||
export const PluginConfigPage = (props: Props) => {
|
||||
const grafanaUrlDefault = getItem('grafanaUrl') || window.location.origin;
|
||||
const { plugin } = props;
|
||||
const [onCallApiUrl, setOnCallApiUrl] = useState<string>(getItem('onCallApiUrl'));
|
||||
const [onCallInvitationToken, setOnCallInvitationToken] = useState<string>();
|
||||
const [grafanaUrl, setGrafanaUrl] = useState<string>(window.location.origin);
|
||||
const [grafanaUrl, setGrafanaUrl] = useState<string>(grafanaUrlDefault);
|
||||
const [pluginConfigLoading, setPluginConfigLoading] = useState<boolean>(true);
|
||||
const [pluginStatusOk, setPluginStatusOk] = useState<boolean>();
|
||||
const [pluginStatusMessage, setPluginStatusMessage] = useState<string>();
|
||||
const [isSelfHostedInstall, setIsSelfHostedInstall] = useState<boolean>(true);
|
||||
const [retrySync, setRetrySync] = useState<boolean>(false);
|
||||
const [showConfirmationModal, setShowConfirmationModal] = useState<boolean>(false);
|
||||
|
||||
const configurePlugin = () => {
|
||||
setShowConfirmationModal(true);
|
||||
};
|
||||
const setupPlugin = useCallback(async () => {
|
||||
setItem('onCallApiUrl', onCallApiUrl);
|
||||
setShowConfirmationModal(false);
|
||||
setItem('grafanaUrl', grafanaUrl);
|
||||
await getBackendSrv().post(`/api/plugins/grafana-oncall-app/settings`, {
|
||||
enabled: true,
|
||||
pinned: true,
|
||||
|
|
@ -218,35 +216,50 @@ export const PluginConfigPage = (props: Props) => {
|
|||
<img alt="Grafana OnCall Logo" src={logo} width={18} />
|
||||
</p>
|
||||
)}
|
||||
<p>{'Plugin <-> backend connection status'}</p>
|
||||
<pre>
|
||||
<Text type="link">{pluginStatusMessage}</Text>
|
||||
</pre>
|
||||
|
||||
{isSelfHostedInstall ? (
|
||||
<div>
|
||||
<p>{'Plugin <-> backend connection status:'}</p>
|
||||
<HorizontalGroup>
|
||||
{/* <p>{'Plugin <-> backend connection status'}</p>
|
||||
<pre>
|
||||
<Text type="link">{pluginStatusMessage}</Text>
|
||||
</pre>
|
||||
</pre> */}
|
||||
{retrySync && (
|
||||
<Button variant="primary" onClick={startSync} size="md">
|
||||
Retry
|
||||
</Button>
|
||||
)}
|
||||
{isSelfHostedInstall ? (
|
||||
<WithConfirm title="Are you sure to delete OnCall plugin configuration?">
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={resetPlugin}
|
||||
size="md"
|
||||
className={cx('delete_configuration_button')}
|
||||
>
|
||||
<Button variant="destructive" onClick={resetPlugin} size="md">
|
||||
Remove current configuration
|
||||
</Button>
|
||||
</WithConfirm>
|
||||
</div>
|
||||
) : (
|
||||
<Label>This is a cloud managed configuration.</Label>
|
||||
)}
|
||||
) : (
|
||||
<Label>This is a cloud managed configuration.</Label>
|
||||
)}{' '}
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Legend>Configure Grafana OnCall</Legend>
|
||||
<p>This page will help you to connect OnCall backend and OnCall Grafana plugin 👋</p>
|
||||
<p>
|
||||
|
||||
<p>1. Launch backend</p>
|
||||
<VerticalGroup>
|
||||
<Text type="secondary">
|
||||
- Talk to the OnCall team in the #grafana-oncall channel at{' '}
|
||||
Run hobby, dev or production backend:{' '}
|
||||
<a href="https://github.com/grafana/oncall#getting-started">
|
||||
<Text type="link">getting started.</Text>
|
||||
</a>
|
||||
</Text>
|
||||
</VerticalGroup>
|
||||
<Block withBackground className={cx('info-block')}>
|
||||
<Text type="secondary">
|
||||
Need help?
|
||||
<br />- Talk to the OnCall team in the #grafana-oncall channel at{' '}
|
||||
<a href="https://slack.grafana.com/">
|
||||
<Text type="link">Slack</Text>
|
||||
</a>
|
||||
|
|
@ -259,17 +272,8 @@ export const PluginConfigPage = (props: Props) => {
|
|||
<Text type="link">GitHub Issues</Text>
|
||||
</a>
|
||||
</Text>
|
||||
</p>
|
||||
<p>1. Launch backend</p>
|
||||
<p>
|
||||
<Text type="secondary">
|
||||
Run hobby, dev or production backend:{' '}
|
||||
<a href="https://github.com/grafana/oncall#getting-started">
|
||||
<Text type="link">getting started.</Text>
|
||||
</a>
|
||||
</Text>
|
||||
</p>
|
||||
<p></p>
|
||||
</Block>
|
||||
|
||||
<p>2. Conect the backend and the plugin </p>
|
||||
<p>{'Plugin <-> backend connection status:'}</p>
|
||||
<pre>
|
||||
|
|
@ -292,42 +296,29 @@ Seek for such a line: “Your invite token: <<LONG TOKEN>> , use it in the Graf
|
|||
|
||||
<Field
|
||||
label="OnCall backend URL"
|
||||
description="It should be reachable from Grafana. Possible options:
|
||||
http://host.docker.internal:8000 (if you run backend in the docker locally)
|
||||
http://localhost:8000
|
||||
..."
|
||||
description={
|
||||
<Text>
|
||||
It should be rechable from Grafana. Possible options: <br />
|
||||
http://host.docker.internal:8000 (if you run backend in the docker locally)
|
||||
<br />
|
||||
http://localhost:8000 <br />
|
||||
...
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<Input id="onCallApiUrl" onChange={handleApiUrlChange} defaultValue={onCallApiUrl} />
|
||||
</Field>
|
||||
<Field label="Grafana Url" description="URL of the current Grafana instance. ">
|
||||
<Field label="Grafana URL" description="URL of the current Grafana instance. ">
|
||||
<Input id="grafanaUrl" onChange={handleGrafanaUrlChange} defaultValue={grafanaUrl} />
|
||||
</Field>
|
||||
{/* <WithConfirm title="Admin API key for OnCall will be created in Grafana. Continue?" confirmText="Continue"> */}
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={configurePlugin}
|
||||
onClick={setupPlugin}
|
||||
disabled={!onCallApiUrl || !onCallInvitationToken || !grafanaUrl}
|
||||
size="md"
|
||||
>
|
||||
Connect
|
||||
</Button>
|
||||
{/* </WithConfirm> */}
|
||||
{showConfirmationModal && (
|
||||
<Modal
|
||||
isOpen
|
||||
title="Admin API key for OnCall will be created in Grafana. Continue?"
|
||||
onDismiss={() => setShowConfirmationModal(false)}
|
||||
>
|
||||
<HorizontalGroup>
|
||||
<Button variant="primary" onClick={setupPlugin}>
|
||||
Continue
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={() => setShowConfirmationModal(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</Modal>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ const CloudPage = observer((props: CloudPageProps) => {
|
|||
</Button>
|
||||
) : (
|
||||
<Button variant="primary" onClick={syncUsers} icon="sync">
|
||||
Sync users
|
||||
Sync users (Editors and Admins)
|
||||
</Button>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
|
|
@ -351,7 +351,7 @@ const CloudPage = observer((props: CloudPageProps) => {
|
|||
<Icon name="bell" className={cx('block-icon')} size="lg" /> SMS and phone call notifications
|
||||
</Text.Title>
|
||||
|
||||
<Text type="secondary">Users matched between OSS and Cloud OnCall currently unavialable.</Text>
|
||||
<Text type="secondary">Users matched between OSS and Cloud OnCall currently unavailable.</Text>
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
</VerticalGroup>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue