From 4cec8eefc21460c7821eab746457f3b64f292bf3 Mon Sep 17 00:00:00 2001 From: Richard Hartmann Date: Fri, 3 Jun 2022 16:19:05 +0200 Subject: [PATCH 01/14] Add Grafana Labs governance and maintainers Signed-off-by: Richard Hartmann --- GOVERNANCE.md | 164 +++++++++++++++++++++++++++++++++++++++++++++++++ MAINTAINERS.md | 15 +++++ 2 files changed, 179 insertions(+) create mode 100644 GOVERNANCE.md create mode 100644 MAINTAINERS.md diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 00000000..830980c8 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,164 @@ +--- +title: Governance +--- + +# Governance + +This document describes the rules and governance of the project. It is meant to be followed by all the developers of the project and the OnCall community. Common terminology used in this governance document are listed below: + +- **Team members**: Any members of the private [team mailing list][team]. + +- **Maintainers**: Maintainers lead an individual project or parts thereof ([`MAINTAINERS.md`][maintainers]). + +- **Projects**: A single repository in the Grafana GitHub organization and listed below is referred to as a project: + + - oncall + +- **The OnCall project**: The sum of all activities performed under this governance, concerning one or more repositories or the community. + +## Values + +The OnCall developers and community are expected to follow the values defined in the [Code of Conduct][coc]. Furthermore, the OnCall community strives for kindness, giving feedback effectively, and building a welcoming environment. The OnCall developers generally decide by consensus and only resort to conflict resolution by a majority vote if consensus cannot be reached. + +## Projects + +Each project must have a [`MAINTAINERS.md`][maintainers] file with at least one maintainer. Where a project has a release process, access and documentation should be such that more than one person can perform a release. Releases should be announced on the [announcemount][announce] and [users][users] mailing lists. Any new projects should be first proposed on the [team mailing list][team] following the voting procedures listed below. + +## Decision making + +### Team members + +Team member status may be given to those who have made ongoing contributions to the OnCall project for at least 3 months. This is usually in the form of code improvements and/or notable work on documentation, but organizing events or user support could also be taken into account. + +New members may be proposed by any existing member by email to the [team mailing list][team]. It is highly desirable to reach consensus about acceptance of a new member. However, the proposal is ultimately voted on by a formal [supermajority vote](#supermajority-vote). + +If the new member proposal is accepted, the proposed team member should be contacted privately via email to confirm or deny their acceptance of team membership. This email will also be CC'd to the [team mailing list][team] for record-keeping purposes. + +If they choose to accept, the [onboarding](#onboarding) procedure is followed. + +Team members may retire at any time by emailing [the team][team]. + +Team members can be removed by [supermajority vote](#supermajority-vote) on [the team mailing list][team]. +For this vote, the member in question is not eligible to vote and does not count towards the quorum. +Any removal vote can cover only one single person. + +Upon death of a member, they leave the team automatically. + +In case a member leaves, the [offboarding](#offboarding) procedure is applied. + +The current team members are: + +- Eve Meelan — [@Eve832](https://github.com/Eve832) ([Grafana Labs](https://grafana.com/)) +- Ildar Iskhakov — [@iskhakov](https://github.com/iskhakov) ([Grafana Labs](https://grafana.com/)) +- Innokentii Konstantinov — [@Konstantinov-Innokentii](https://github.com/Konstantinov-Innokentii) ([Grafana Labs](https://grafana.com/)) +- Matías Bordese — [@matiasb](https://github.com/matiasb) ([Grafana Labs](https://grafana.com/)) +- Matvey Kuku — [@Matvey-Kuk](https://github.com/Matvey-Kuk) ([Grafana Labs](https://grafana.com/)) +- Michael Derynck — [@mderynck](https://github.com/mderynck) ([Grafana Labs](https://grafana.com/)) +- Vadim Stepanov — [@vadimkerr](https://github.com/vadimkerr) ([Grafana Labs](https://grafana.com/)) +- Yulia Shanyrova — [@Ukochka](https://github.com/Ukochka) ([Grafana Labs](https://grafana.com/)) + +Previous team members: + +- n/a + +### Maintainers + +Maintainers lead one or more project(s) or parts thereof and serve as a point of conflict resolution amongst the contributors to this project. Ideally, maintainers are also team members, but exceptions are possible for suitable maintainers that, for whatever reason, are not yet team members. + +Changes in maintainership have to be announced on the [developers mailing list][devs]. They are decided by [rough consensus](#consensus) and formalized by changing the [`MAINTAINERS.md`][maintainers] file of the respective repository. + +Maintainers are granted commit rights to all projects covered by this governance. + +A maintainer or committer may resign by notifying the [team mailing list][team]. A maintainer with no project activity for a year is considered to have resigned. Maintainers that wish to resign are encouraged to propose another team member to take over the project. + +A project may have multiple maintainers, as long as the responsibilities are clearly agreed upon between them. This includes coordinating who handles which issues and pull requests. + +### Technical decisions + +Technical decisions that only affect a single project are made informally by the maintainer of this project, and [rough consensus](#consensus) is assumed. Technical decisions that span multiple parts of the project should be discussed and made on the [developer mailing list][devs]. + +Decisions are usually made by [rough consensus](#consensus). If no consensus can be reached, the matter may be resolved by [majority vote](#majority-vote). + +### Governance changes + +Changes to this document are made by Grafana Labs. + +### Other matters + +Any matter that needs a decision may be called to a vote by any member if they deem it necessary. For private or personnel matters, discussion and voting takes place on the [team mailing list][team], otherwise on the [developer mailing list][devs]. + +## Voting + +The OnCall project usually runs by informal consensus, however sometimes a formal decision must be made. + +Depending on the subject matter, as laid out [above](#decision-making), different methods of voting are used. + +For all votes, voting must be open for at least one week. The end date should be clearly stated in the call to vote. A vote may be called and closed early if enough votes have come in one way so that further votes cannot change the final decision. + +In all cases, all and only [team members](#team-members) are eligible to vote, with the sole exception of the forced removal of a team member, in which said member is not eligible to vote. + +Discussion and votes on personnel matters (including but not limited to team membership and maintainership) are held in private on the [team mailing list][team]. All other discussion and votes are held in public on the [developer mailing list][devs]. + +For public discussions, anyone interested is encouraged to participate. Formal power to object or vote is limited to [team members](#team-members). + +### Consensus + +The default decision making mechanism for the OnCall project is [rough][rough] consensus. This means that any decision on technical issues is considered supported by the [team][team] as long as nobody objects or the objection has been considered but not necessarily accommodated. + +Silence on any consensus decision is implicit agreement and equivalent to explicit agreement. Explicit agreement may be stated at will. Decisions may, but do not need to be called out and put up for decision on the [developers mailing list][devs] at any time and by anyone. + +Consensus decisions can never override or go against the spirit of an earlier explicit vote. + +If any [team member](#team-members) raises objections, the team members work together towards a solution that all involved can accept. This solution is again subject to rough consensus. + +In case no consensus can be found, but a decision one way or the other must be made, any [team member](#team-members) may call a formal [majority vote](#majority-vote). + +### Majority vote + +Majority votes must be called explicitly in a separate thread on the appropriate mailing list. The subject must be prefixed with `[VOTE]`. In the body, the call to vote must state the proposal being voted on. It should reference any discussion leading up to this point. + +Votes may take the form of a single proposal, with the option to vote yes or no, or the form of multiple alternatives. + +A vote on a single proposal is considered successful if more vote in favor than against. + +If there are multiple alternatives, members may vote for one or more alternatives, or vote “no” to object to all alternatives. It is not possible to cast an “abstain” vote. A vote on multiple alternatives is considered decided in favor of one alternative if it has received the most votes in favor, and a vote from more than half of those voting. Should no alternative reach this quorum, another vote on a reduced number of options may be called separately. + +### Supermajority vote + +Supermajority votes must be called explicitly in a separate thread on the appropriate mailing list. The subject must be prefixed with `[VOTE]`. In the body, the call to vote must state the proposal being voted on. It should reference any discussion leading up to this point. + +Votes may take the form of a single proposal, with the option to vote yes or no, or the form of multiple alternatives. + +A vote on a single proposal is considered successful if at least two thirds of those eligible to vote vote in favor. + +If there are multiple alternatives, members may vote for one or more alternatives, or vote “no” to object to all alternatives. A vote on multiple alternatives is considered decided in favor of one alternative if it has received the most votes in favor, and a vote from at least two thirds of those eligible to vote. Should no alternative reach this quorum, another vote on a reduced number of options may be called separately. + +## On- / Offboarding + +### Onboarding + +The new member is + +- added to the list of [team members](#team-members). Ideally by sending a PR of their own, at least approving said PR. +- announced on the [developers mailing list][devs] by an existing team member. Ideally, the new member replies in this thread, acknowledging team membership. +- added to the projects with commit rights. +- added to the [team mailing list][team]. + +### Offboarding + +The ex-member is + +- removed from the list of [team members](#team-members). Ideally by sending a PR of their own, at least approving said PR. In case of forced removal, no approval is needed. +- removed from the projects. Optionally, they can retain maintainership of one or more repositories if the [team](#team-members) agrees. +- removed from the team mailing list and demoted to a normal member of the other mailing lists. +- not allowed to call themselves an active team member any more, nor allowed to imply this to be the case. +- added to a list of previous members if they so choose. + +If needed, we reserve the right to publicly announce removal. + +[announce]: TODO +[coc]: https://github.com/grafana/oncall/blob/master/CODE_OF_CONDUCT.md +[devs]: TODO +[maintainers]: https://github.com/grafana/oncall/blob/master/MAINTAINERS.md +[rough]: https://tools.ietf.org/html/rfc7282 +[team]: TODO diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 00000000..97c9ba33 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,15 @@ +The following are the main/default maintainers: + +- Ildar Iskhakov — [@iskhakov](https://github.com/iskhakov) ([Grafana Labs](https://grafana.com/)) +- Matvey Kuku — [@Matvey-Kuk](https://github.com/Matvey-Kuk) ([Grafana Labs](https://grafana.com/)) + +Some parts of the codebase have other maintainers, the package paths also include all sub-packages: + +- `docs`: + - Eve Meelan — [@Eve832](https://github.com/Eve832) ([Grafana Labs](https://grafana.com/)) + +For the sake of brevity, not all subtrees are explicitly listed. Due to the +size of this repository, the natural changes in focus of maintainers over time, +and nuances of where particular features live, this list will always be +incomplete and out of date. However the listed maintainer(s) should be able to +direct a PR/question to the right person. From 818be0a5f8b422bd47edd2bb26f081b8a1865620 Mon Sep 17 00:00:00 2001 From: Richard Hartmann Date: Fri, 3 Jun 2022 16:21:26 +0200 Subject: [PATCH 02/14] Making LICENSING.md more explicit Signed-off-by: Richard Hartmann --- LICENSING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LICENSING.md b/LICENSING.md index 4e53ac0d..34951583 100644 --- a/LICENSING.md +++ b/LICENSING.md @@ -9,9 +9,11 @@ The default license for this project is [AGPL-3.0-only](LICENSE). The following directories and their subdirectories are licensed under Apache-2.0: ``` +n/a ``` The following directories and their subdirectories are licensed under their original upstream licenses: ``` +n/a ``` From 9f8989726476b7a8dff41ec1abd7fb686e2c0763 Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Mon, 6 Jun 2022 12:29:25 -0300 Subject: [PATCH 03/14] Drop data migrations setting up demo tokens --- ...03_squashed_create_demo_token_instances.py | 178 ------------------ ...03_squashed_create_demo_token_instances.py | 40 ---- ...03_squashed_create_demo_token_instances.py | 74 -------- ...03_squashed_create_demo_token_instances.py | 47 ----- ...02_squashed_create_demo_token_instances.py | 51 ----- 5 files changed, 390 deletions(-) delete mode 100644 engine/apps/alerts/migrations/0003_squashed_create_demo_token_instances.py delete mode 100644 engine/apps/auth_token/migrations/0003_squashed_create_demo_token_instances.py delete mode 100644 engine/apps/base/migrations/0003_squashed_create_demo_token_instances.py delete mode 100644 engine/apps/slack/migrations/0003_squashed_create_demo_token_instances.py delete mode 100644 engine/apps/user_management/migrations/0002_squashed_create_demo_token_instances.py diff --git a/engine/apps/alerts/migrations/0003_squashed_create_demo_token_instances.py b/engine/apps/alerts/migrations/0003_squashed_create_demo_token_instances.py deleted file mode 100644 index 5729cbd6..00000000 --- a/engine/apps/alerts/migrations/0003_squashed_create_demo_token_instances.py +++ /dev/null @@ -1,178 +0,0 @@ -# Generated by Django 3.2.5 on 2021-08-04 10:42 - -import sys -from django.db import migrations -from django.utils import timezone, dateparse -from apps.alerts.models.alert_receive_channel import number_to_smiles_translator -from apps.public_api import constants as public_api_constants - - -TYPE_SINGLE_EVENT = 0 -TYPE_RECURRENT_EVENT = 1 -FREQUENCY_WEEKLY = 1 -SOURCE_TERRAFORM = 3 -STEP_WAIT = 0 -STEP_NOTIFY_USERS_QUEUE = 12 -SOURCE_WEB = 1 - - -def create_demo_token_instances(apps, schema_editor): - if not (len(sys.argv) > 1 and sys.argv[1] == 'test'): - User = apps.get_model('user_management', 'User') - Organization = apps.get_model('user_management', 'Organization') - AlertReceiveChannel = apps.get_model('alerts', 'AlertReceiveChannel') - EscalationChain = apps.get_model('alerts', 'EscalationChain') - ChannelFilter = apps.get_model('alerts', 'ChannelFilter') - EscalationPolicy = apps.get_model('alerts', 'EscalationPolicy') - OnCallScheduleICal = apps.get_model('schedules', 'OnCallScheduleICal') - AlertGroup = apps.get_model('alerts', 'AlertGroup') - Alert = apps.get_model('alerts', 'Alert') - CustomButton = apps.get_model("alerts", "CustomButton") - CustomOnCallShift = apps.get_model('schedules', 'CustomOnCallShift') - - organization = Organization.objects.get(public_primary_key=public_api_constants.DEMO_ORGANIZATION_ID) - user = User.objects.get(public_primary_key=public_api_constants.DEMO_USER_ID) - - alert_receive_channel, _ = AlertReceiveChannel.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_INTEGRATION_ID, - defaults=dict( - integration=0, - author=user, - organization=organization, - smile_code=number_to_smiles_translator(0) - ) - ) - escalation_chain, _ = EscalationChain.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ESCALATION_CHAIN_ID, - defaults=dict( - name="default", - organization=organization, - ) - ) - - channel_filter_1, _ = ChannelFilter.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ROUTE_ID_1, - defaults=dict( - alert_receive_channel=alert_receive_channel, - slack_channel_id=public_api_constants.DEMO_SLACK_CHANNEL_FOR_ROUTE_ID, - filtering_term='us-(east|west)', - order=0, - escalation_chain=escalation_chain, - ) - ) - ChannelFilter.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ROUTE_ID_2, - defaults=dict( - alert_receive_channel=alert_receive_channel, - slack_channel_id=public_api_constants.DEMO_SLACK_CHANNEL_FOR_ROUTE_ID, - filtering_term='.*', - order=1, - is_default=True, - escalation_chain=escalation_chain, - ) - ) - - EscalationPolicy.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ESCALATION_POLICY_ID_1, - defaults=dict( - step=STEP_WAIT, - wait_delay=timezone.timedelta(minutes=1), - order=0, - escalation_chain=escalation_chain, - ) - ) - - escalation_policy_1, _ = EscalationPolicy.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ESCALATION_POLICY_ID_2, - defaults=dict( - step=STEP_NOTIFY_USERS_QUEUE, - order=1, - escalation_chain=escalation_chain, - ) - ) - escalation_policy_1.notify_to_users_queue.add(user) - - schedule, _ = OnCallScheduleICal.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_SCHEDULE_ID_ICAL, - defaults=dict( - organization=organization, - name=public_api_constants.DEMO_SCHEDULE_NAME_ICAL, - ical_url_overrides=public_api_constants.DEMO_SCHEDULE_ICAL_URL_OVERRIDES, - channel=public_api_constants.DEMO_SLACK_CHANNEL_SLACK_ID, - ) - ) - - alert_group, _ = AlertGroup.all_objects.get_or_create( - public_primary_key=public_api_constants.DEMO_INCIDENT_ID, - defaults=dict( - channel=alert_receive_channel, - channel_filter=channel_filter_1, - resolved=True, - resolved_at=dateparse.parse_datetime(public_api_constants.DEMO_INCIDENT_RESOLVED_AT), - ) - ) - alert_group.started_at = dateparse.parse_datetime(public_api_constants.DEMO_INCIDENT_CREATED_AT) - alert_group.save(update_fields=['started_at']) - - for id, created_at in public_api_constants.DEMO_ALERT_IDS: - alert, _ = Alert.objects.get_or_create( - public_primary_key=id, - defaults=dict( - group=alert_group, - raw_request_data=public_api_constants.DEMO_ALERT_PAYLOAD, - title='Memory above 90% threshold', - ) - ) - alert.created_at = dateparse.parse_datetime(created_at) - alert.save(update_fields=['created_at']) - - CustomButton.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_CUSTOM_ACTION_ID, - defaults=dict( - name=public_api_constants.DEMO_CUSTOM_ACTION_NAME, - organization=organization, - ) - ) - - on_call_shift_1, _ = CustomOnCallShift.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ON_CALL_SHIFT_ID_1, - defaults=dict( - type=TYPE_SINGLE_EVENT, - organization=organization, - name=public_api_constants.DEMO_ON_CALL_SHIFT_NAME_1, - start=dateparse.parse_datetime(public_api_constants.DEMO_ON_CALL_SHIFT_START_1), - duration=timezone.timedelta(seconds=public_api_constants.DEMO_ON_CALL_SHIFT_DURATION), - ) - ) - - on_call_shift_1.users.add(user) - - on_call_shift_2, _ = CustomOnCallShift.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ON_CALL_SHIFT_ID_2, - defaults=dict( - type=TYPE_RECURRENT_EVENT, - organization=organization, - name=public_api_constants.DEMO_ON_CALL_SHIFT_NAME_2, - start=dateparse.parse_datetime(public_api_constants.DEMO_ON_CALL_SHIFT_START_2), - duration=timezone.timedelta(seconds=public_api_constants.DEMO_ON_CALL_SHIFT_DURATION), - frequency=FREQUENCY_WEEKLY, - interval=2, - by_day=public_api_constants.DEMO_ON_CALL_SHIFT_BY_DAY, - source=SOURCE_TERRAFORM, - ) - ) - - on_call_shift_2.users.add(user) - - -class Migration(migrations.Migration): - - dependencies = [ - ('alerts', '0002_squashed_initial'), - ('user_management', '0002_squashed_create_demo_token_instances'), - ('schedules', '0002_squashed_initial'), - ] - - operations = [ - migrations.RunPython(create_demo_token_instances, migrations.RunPython.noop) - ] diff --git a/engine/apps/auth_token/migrations/0003_squashed_create_demo_token_instances.py b/engine/apps/auth_token/migrations/0003_squashed_create_demo_token_instances.py deleted file mode 100644 index 225e0fcb..00000000 --- a/engine/apps/auth_token/migrations/0003_squashed_create_demo_token_instances.py +++ /dev/null @@ -1,40 +0,0 @@ -# Generated by Django 3.2.5 on 2021-08-04 13:02 - -import sys -from django.db import migrations - -from apps.auth_token import constants -from apps.auth_token import crypto -from apps.public_api import constants as public_api_constants - - -def create_demo_token_instances(apps, schema_editor): - if not (len(sys.argv) > 1 and sys.argv[1] == 'test'): - User = apps.get_model('user_management', 'User') - Organization = apps.get_model('user_management', 'Organization') - ApiAuthToken = apps.get_model('auth_token', 'ApiAuthToken') - - organization = Organization.objects.get(public_primary_key=public_api_constants.DEMO_ORGANIZATION_ID) - user = User.objects.get(public_primary_key=public_api_constants.DEMO_USER_ID) - - token_string = crypto.generate_token_string() - digest = crypto.hash_token_string(token_string) - - ApiAuthToken.objects.get_or_create( - name=public_api_constants.DEMO_AUTH_TOKEN, - user=user, - organization=organization, - defaults=dict(token_key=token_string[:constants.TOKEN_KEY_LENGTH], digest=digest) - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('auth_token', '0002_squashed_initial'), - ('user_management', '0002_squashed_create_demo_token_instances') - ] - - operations = [ - migrations.RunPython(create_demo_token_instances, migrations.RunPython.noop) - ] diff --git a/engine/apps/base/migrations/0003_squashed_create_demo_token_instances.py b/engine/apps/base/migrations/0003_squashed_create_demo_token_instances.py deleted file mode 100644 index a590210a..00000000 --- a/engine/apps/base/migrations/0003_squashed_create_demo_token_instances.py +++ /dev/null @@ -1,74 +0,0 @@ -# Generated by Django 3.2.5 on 2021-08-04 10:45 - -import sys -from django.db import migrations -from django.utils import timezone -from apps.public_api import constants as public_api_constants - - -STEP_WAIT = 0 -STEP_NOTIFY = 1 -NOTIFY_BY_SMS = 1 -NOTIFY_BY_PHONE = 2 -FIVE_MINUTES = timezone.timedelta(minutes=5) - - -def create_demo_token_instances(apps, schema_editor): - if not (len(sys.argv) > 1 and sys.argv[1] == 'test'): - User = apps.get_model('user_management', 'User') - UserNotificationPolicy = apps.get_model("base", "UserNotificationPolicy") - - user = User.objects.get(public_primary_key=public_api_constants.DEMO_USER_ID) - - UserNotificationPolicy.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_PERSONAL_NOTIFICATION_ID_1, - defaults=dict( - important=False, - user=user, - notify_by=NOTIFY_BY_SMS, - step=STEP_NOTIFY, - order=0, - ) - ) - UserNotificationPolicy.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_PERSONAL_NOTIFICATION_ID_2, - defaults=dict( - important=False, - user=user, - step=STEP_WAIT, - wait_delay=FIVE_MINUTES, - order=1, - ) - ) - UserNotificationPolicy.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_PERSONAL_NOTIFICATION_ID_3, - defaults=dict( - important=False, - user=user, - step=STEP_NOTIFY, - notify_by=NOTIFY_BY_PHONE, - order=2, - ) - ) - - UserNotificationPolicy.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_PERSONAL_NOTIFICATION_ID_4, - defaults=dict( - important=True, - user=user, - notify_by=NOTIFY_BY_PHONE, - order=0, - ) - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0002_squashed_initial'), - ('user_management', '0002_squashed_create_demo_token_instances') - ] - - operations = [ - migrations.RunPython(create_demo_token_instances, migrations.RunPython.noop) - ] diff --git a/engine/apps/slack/migrations/0003_squashed_create_demo_token_instances.py b/engine/apps/slack/migrations/0003_squashed_create_demo_token_instances.py deleted file mode 100644 index ae3368f1..00000000 --- a/engine/apps/slack/migrations/0003_squashed_create_demo_token_instances.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 3.2.5 on 2021-08-04 10:51 - -import sys -from django.db import migrations -from apps.public_api import constants as public_api_constants - - -def create_demo_token_instances(apps, schema_editor): - if not (len(sys.argv) > 1 and sys.argv[1] == 'test'): - SlackUserIdentity = apps.get_model('slack', 'SlackUserIdentity') - SlackTeamIdentity = apps.get_model('slack', 'SlackTeamIdentity') - SlackChannel = apps.get_model('slack', 'SlackChannel') - SlackUserGroup = apps.get_model("slack", "SlackUserGroup") - - slack_team_identity, _ = SlackTeamIdentity.objects.get_or_create( - slack_id=public_api_constants.DEMO_SLACK_TEAM_ID, - ) - SlackUserIdentity.objects.get_or_create( - slack_id=public_api_constants.DEMO_SLACK_USER_ID, - slack_team_identity=slack_team_identity, - ) - - SlackChannel.objects.get_or_create( - name=public_api_constants.DEMO_SLACK_CHANNEL_NAME, - slack_id=public_api_constants.DEMO_SLACK_CHANNEL_SLACK_ID, - slack_team_identity=slack_team_identity, - ) - - SlackUserGroup.objects.get_or_create( - slack_team_identity=slack_team_identity, - slack_id=public_api_constants.DEMO_SLACK_USER_GROUP_SLACK_ID, - public_primary_key=public_api_constants.DEMO_SLACK_USER_GROUP_ID, - name=public_api_constants.DEMO_SLACK_USER_GROUP_NAME, - handle=public_api_constants.DEMO_SLACK_USER_GROUP_HANDLE, - is_active=True, - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('slack', '0002_squashed_initial'), - ] - - operations = [ - migrations.RunPython(create_demo_token_instances, migrations.RunPython.noop) - ] diff --git a/engine/apps/user_management/migrations/0002_squashed_create_demo_token_instances.py b/engine/apps/user_management/migrations/0002_squashed_create_demo_token_instances.py deleted file mode 100644 index 8b8f932c..00000000 --- a/engine/apps/user_management/migrations/0002_squashed_create_demo_token_instances.py +++ /dev/null @@ -1,51 +0,0 @@ -# Generated by Django 3.2.5 on 2021-08-04 10:46 - -import sys -from django.db import migrations -from apps.public_api import constants as public_api_constants -from common.constants.role import Role - - -def create_demo_token_instances(apps, schema_editor): - if not (len(sys.argv) > 1 and sys.argv[1] == 'test'): - SlackUserIdentity = apps.get_model('slack', 'SlackUserIdentity') - SlackTeamIdentity = apps.get_model('slack', 'SlackTeamIdentity') - User = apps.get_model('user_management', 'User') - Organization = apps.get_model('user_management', 'Organization') - - slack_team_identity = SlackTeamIdentity.objects.get(slack_id=public_api_constants.DEMO_SLACK_TEAM_ID) - slack_user_identity = SlackUserIdentity.objects.get( - slack_id=public_api_constants.DEMO_SLACK_USER_ID, - slack_team_identity=slack_team_identity, - ) - - organization, _ = Organization.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_ORGANIZATION_ID, - defaults=dict( - slack_team_identity=slack_team_identity, - org_id=0, stack_id=0, - ) - ) - User.objects.get_or_create( - public_primary_key=public_api_constants.DEMO_USER_ID, - defaults=dict( - username=public_api_constants.DEMO_USER_USERNAME, - email=public_api_constants.DEMO_USER_EMAIL, - organization=organization, - role=Role.ADMIN, - slack_user_identity=slack_user_identity, - user_id=0, - ) - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('user_management', '0001_squashed_initial'), - ('slack', '0003_squashed_create_demo_token_instances'), - ] - - operations = [ - migrations.RunPython(create_demo_token_instances, migrations.RunPython.noop) - ] From b3fed35550454226a7ed9f80dff2a1333fc9fa80 Mon Sep 17 00:00:00 2001 From: Michael Derynck Date: Mon, 6 Jun 2022 10:13:17 -0600 Subject: [PATCH 04/14] Fix typo in alertgroups url --- grafana-plugin/src/models/alertgroup/alertgroup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana-plugin/src/models/alertgroup/alertgroup.ts b/grafana-plugin/src/models/alertgroup/alertgroup.ts index 684948a1..ba284236 100644 --- a/grafana-plugin/src/models/alertgroup/alertgroup.ts +++ b/grafana-plugin/src/models/alertgroup/alertgroup.ts @@ -68,7 +68,7 @@ export class AlertGroupStore extends BaseStore { constructor(rootStore: RootStore) { super(rootStore); - this.path = '/alertgroups1/'; + this.path = '/alertgroups/'; } async attachAlert(pk: Alert['pk'], rootPk: Alert['pk']) { From dbaffd86f529bcb50fc052e67bd2d89329e951b2 Mon Sep 17 00:00:00 2001 From: Michael Derynck Date: Mon, 6 Jun 2022 11:02:09 -0600 Subject: [PATCH 05/14] Revert message on alert group page while loading --- grafana-plugin/src/pages/incidents/Incidents.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grafana-plugin/src/pages/incidents/Incidents.tsx b/grafana-plugin/src/pages/incidents/Incidents.tsx index 9ba1dd71..6e8f3f72 100644 --- a/grafana-plugin/src/pages/incidents/Incidents.tsx +++ b/grafana-plugin/src/pages/incidents/Incidents.tsx @@ -302,12 +302,11 @@ class Incidents extends React.Component (results && results.some((alert: AlertType) => alert.undoAction)) || Object.keys(affectedRows).length ); - console.log('results', results); return (
{this.renderBulkActions()} Date: Mon, 6 Jun 2022 12:04:31 -0600 Subject: [PATCH 06/14] Fix too many tasks being created for create contact points (#12) Fix too many tasks being created for create_contact_points_for_datasource task Co-authored-by: Julia --- .../grafana_alerting_sync.py | 9 ++-- engine/apps/alerts/tasks/__init__.py | 1 + .../create_contact_points_for_datasource.py | 42 +++++++++++++++++-- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/engine/apps/alerts/grafana_alerting_sync_manager/grafana_alerting_sync.py b/engine/apps/alerts/grafana_alerting_sync_manager/grafana_alerting_sync.py index 7bfcbdef..a9ca08fb 100644 --- a/engine/apps/alerts/grafana_alerting_sync_manager/grafana_alerting_sync.py +++ b/engine/apps/alerts/grafana_alerting_sync_manager/grafana_alerting_sync.py @@ -5,7 +5,7 @@ from typing import Optional from django.apps import apps from rest_framework import status -from apps.alerts.tasks import create_contact_points_for_datasource +from apps.alerts.tasks import schedule_create_contact_points_for_datasource from apps.grafana_plugin.helpers import GrafanaAPIClient logger = logging.getLogger(__name__) @@ -77,16 +77,15 @@ class GrafanaAlertingSyncManager: # sync other datasource for datasource in datasources: if datasource["type"] == GrafanaAlertingSyncManager.ALERTING_DATASOURCE: - if self.create_contact_point(datasource) is None: + contact_point = self.create_contact_point(datasource) + if contact_point is None: # Failed to create contact point duo to getting wrong alerting config. It is expected behaviour. # Add datasource to list and retry to create contact point for it async datasources_to_create.append(datasource) if datasources_to_create: # create other contact points async - create_contact_points_for_datasource.apply_async( - (self.alert_receive_channel.pk, datasources_to_create), - ) + schedule_create_contact_points_for_datasource(self.alert_receive_channel.pk, datasources_to_create) else: self.alert_receive_channel.is_finished_alerting_setup = True self.alert_receive_channel.save(update_fields=["is_finished_alerting_setup"]) diff --git a/engine/apps/alerts/tasks/__init__.py b/engine/apps/alerts/tasks/__init__.py index 8e0e994f..3ff8501e 100644 --- a/engine/apps/alerts/tasks/__init__.py +++ b/engine/apps/alerts/tasks/__init__.py @@ -4,6 +4,7 @@ from .calculcate_escalation_finish_time import calculate_escalation_finish_time from .call_ack_url import call_ack_url # noqa: F401 from .check_escalation_finished import check_escalation_finished_task # noqa: F401 from .create_contact_points_for_datasource import create_contact_points_for_datasource # noqa: F401 +from .create_contact_points_for_datasource import schedule_create_contact_points_for_datasource # noqa: F401 from .custom_button_result import custom_button_result # noqa: F401 from .delete_alert_group import delete_alert_group # noqa: F401 from .distribute_alert import distribute_alert # noqa: F401 diff --git a/engine/apps/alerts/tasks/create_contact_points_for_datasource.py b/engine/apps/alerts/tasks/create_contact_points_for_datasource.py index f3dc3f4b..a447a39c 100644 --- a/engine/apps/alerts/tasks/create_contact_points_for_datasource.py +++ b/engine/apps/alerts/tasks/create_contact_points_for_datasource.py @@ -1,9 +1,32 @@ +import logging + +from celery.utils.log import get_task_logger from django.apps import apps +from django.core.cache import cache from rest_framework import status from apps.grafana_plugin.helpers import GrafanaAPIClient from common.custom_celery_tasks import shared_dedicated_queue_retry_task +logger = get_task_logger(__name__) +logger.setLevel(logging.DEBUG) + + +def get_cache_key_create_contact_points_for_datasource(alert_receive_channel_id): + CACHE_KEY_PREFIX = "create_contact_points_for_datasource" + return f"{CACHE_KEY_PREFIX}_{alert_receive_channel_id}" + + +@shared_dedicated_queue_retry_task +def schedule_create_contact_points_for_datasource(alert_receive_channel_id, datasource_list): + CACHE_LIFETIME = 600 + START_TASK_DELAY = 3 + task = create_contact_points_for_datasource.apply_async( + args=[alert_receive_channel_id, datasource_list], countdown=START_TASK_DELAY + ) + cache_key = get_cache_key_create_contact_points_for_datasource(alert_receive_channel_id) + cache.set(cache_key, task.id, timeout=CACHE_LIFETIME) + @shared_dedicated_queue_retry_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=10) def create_contact_points_for_datasource(alert_receive_channel_id, datasource_list): @@ -11,6 +34,11 @@ def create_contact_points_for_datasource(alert_receive_channel_id, datasource_li Try to create contact points for other datasource. Restart task for datasource, for which contact point was not created. """ + cache_key = get_cache_key_create_contact_points_for_datasource(alert_receive_channel_id) + cached_task_id = cache.get(cache_key) + current_task_id = create_contact_points_for_datasource.request.id + if cached_task_id is not None and current_task_id != cached_task_id: + return AlertReceiveChannel = apps.get_model("alerts", "AlertReceiveChannel") @@ -21,7 +49,7 @@ def create_contact_points_for_datasource(alert_receive_channel_id, datasource_li api_token=alert_receive_channel.organization.api_token, ) # list of datasource for which contact point creation was failed - datasource_to_create = [] + datasources_to_create = [] for datasource in datasource_list: contact_point = None config, response_info = client.get_alerting_config(datasource["id"]) @@ -29,16 +57,22 @@ def create_contact_points_for_datasource(alert_receive_channel_id, datasource_li if response_info.get("status_code") == status.HTTP_404_NOT_FOUND: client.get_alertmanager_status_with_config(datasource["id"]) contact_point = alert_receive_channel.grafana_alerting_sync_manager.create_contact_point(datasource) + elif response_info.get("status_code") == status.HTTP_400_BAD_REQUEST: + logger.warning( + f"Failed to create contact point for integration {alert_receive_channel_id}, " + f"datasource info: {datasource}; response: {response_info}" + ) + continue else: contact_point = alert_receive_channel.grafana_alerting_sync_manager.create_contact_point(datasource) if contact_point is None: # Failed to create contact point duo to getting wrong alerting config. # Add datasource to list and retry to create contact point for it again - datasource_to_create.append(datasource) + datasources_to_create.append(datasource) # if some contact points were not created, restart task for them - if datasource_to_create: - create_contact_points_for_datasource.apply_async((alert_receive_channel_id, datasource_to_create), countdown=5) + if datasources_to_create: + schedule_create_contact_points_for_datasource(alert_receive_channel_id, datasources_to_create) else: alert_receive_channel.is_finished_alerting_setup = True alert_receive_channel.save(update_fields=["is_finished_alerting_setup"]) From 3201a805f19370e03cacd0d62e02fb3731b550eb Mon Sep 17 00:00:00 2001 From: Matvey Kukuy Date: Mon, 6 Jun 2022 21:54:15 +0300 Subject: [PATCH 07/14] Updated GOVERNANCE, added Code Of Conduct --- CODE_OF_CONDUCT.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ GOVERNANCE.md | 27 +++++++++++---------------- MAINTAINERS.md | 5 ++--- 3 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..3d4caa4f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at conduct@grafana.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 830980c8..6b837d68 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -22,7 +22,7 @@ The OnCall developers and community are expected to follow the values defined in ## Projects -Each project must have a [`MAINTAINERS.md`][maintainers] file with at least one maintainer. Where a project has a release process, access and documentation should be such that more than one person can perform a release. Releases should be announced on the [announcemount][announce] and [users][users] mailing lists. Any new projects should be first proposed on the [team mailing list][team] following the voting procedures listed below. +Each project must have a [`MAINTAINERS.md`][maintainers] file with at least one maintainer. Where a project has a release process, access and documentation should be such that more than one person can perform a release. Releases should be announced on the [announcements][https://github.com/grafana/oncall/discussions/categories/announcements] category at the GitHub Discussions. Any new projects should be first proposed on the [team mailing list][team] following the voting procedures listed below. ## Decision making @@ -48,14 +48,16 @@ In case a member leaves, the [offboarding](#offboarding) procedure is applied. The current team members are: -- Eve Meelan — [@Eve832](https://github.com/Eve832) ([Grafana Labs](https://grafana.com/)) - Ildar Iskhakov — [@iskhakov](https://github.com/iskhakov) ([Grafana Labs](https://grafana.com/)) - Innokentii Konstantinov — [@Konstantinov-Innokentii](https://github.com/Konstantinov-Innokentii) ([Grafana Labs](https://grafana.com/)) - Matías Bordese — [@matiasb](https://github.com/matiasb) ([Grafana Labs](https://grafana.com/)) -- Matvey Kuku — [@Matvey-Kuk](https://github.com/Matvey-Kuk) ([Grafana Labs](https://grafana.com/)) +- Matvey Kukuy — [@Matvey-Kuk](https://github.com/Matvey-Kuk) ([Grafana Labs](https://grafana.com/)) - Michael Derynck — [@mderynck](https://github.com/mderynck) ([Grafana Labs](https://grafana.com/)) - Vadim Stepanov — [@vadimkerr](https://github.com/vadimkerr) ([Grafana Labs](https://grafana.com/)) - Yulia Shanyrova — [@Ukochka](https://github.com/Ukochka) ([Grafana Labs](https://grafana.com/)) +- Maxim Mordasov — [@maskin25](https://github.com/maskin25) ([Grafana Labs](https://grafana.com/)) +- Julia Artyukhina — [@Ferril](https://github.com/Ferril) ([Grafana Labs](https://grafana.com/)) +- Julia Artyukhina — [@Ferril](https://github.com/Ferril) ([Grafana Labs](https://grafana.com/)) Previous team members: @@ -65,7 +67,7 @@ Previous team members: Maintainers lead one or more project(s) or parts thereof and serve as a point of conflict resolution amongst the contributors to this project. Ideally, maintainers are also team members, but exceptions are possible for suitable maintainers that, for whatever reason, are not yet team members. -Changes in maintainership have to be announced on the [developers mailing list][devs]. They are decided by [rough consensus](#consensus) and formalized by changing the [`MAINTAINERS.md`][maintainers] file of the respective repository. +Changes in maintainership have to be announced on the [announcemount][https://github.com/grafana/oncall/discussions/categories/announcements] category at the GitHub Discussions. They are decided by [rough consensus](#consensus) and formalized by changing the [`MAINTAINERS.md`][maintainers] file of the respective repository. Maintainers are granted commit rights to all projects covered by this governance. @@ -75,7 +77,7 @@ A project may have multiple maintainers, as long as the responsibilities are cle ### Technical decisions -Technical decisions that only affect a single project are made informally by the maintainer of this project, and [rough consensus](#consensus) is assumed. Technical decisions that span multiple parts of the project should be discussed and made on the [developer mailing list][devs]. +Technical decisions that only affect a single project are made informally by the maintainer of this project, and [rough consensus](#consensus) is assumed. Technical decisions that span multiple parts of the project should be discussed and made on the the [GitHub Discussions][https://github.com/grafana/oncall/discussions]. Decisions are usually made by [rough consensus](#consensus). If no consensus can be reached, the matter may be resolved by [majority vote](#majority-vote). @@ -85,7 +87,7 @@ Changes to this document are made by Grafana Labs. ### Other matters -Any matter that needs a decision may be called to a vote by any member if they deem it necessary. For private or personnel matters, discussion and voting takes place on the [team mailing list][team], otherwise on the [developer mailing list][devs]. +Any matter that needs a decision may be called to a vote by any member if they deem it necessary. For private or personnel matters, discussion and voting takes place on the [team mailing list][team], otherwise on the [GitHub Discussions][https://github.com/grafana/oncall/discussions]. ## Voting @@ -97,7 +99,7 @@ For all votes, voting must be open for at least one week. The end date should be In all cases, all and only [team members](#team-members) are eligible to vote, with the sole exception of the forced removal of a team member, in which said member is not eligible to vote. -Discussion and votes on personnel matters (including but not limited to team membership and maintainership) are held in private on the [team mailing list][team]. All other discussion and votes are held in public on the [developer mailing list][devs]. +Discussion and votes on personnel matters (including but not limited to team membership and maintainership) are held in private on the [team mailing list][team]. All other discussion and votes are held in public on the [GitHub Discussions][https://github.com/grafana/oncall/discussions]. For public discussions, anyone interested is encouraged to participate. Formal power to object or vote is limited to [team members](#team-members). @@ -105,7 +107,7 @@ For public discussions, anyone interested is encouraged to participate. Formal p The default decision making mechanism for the OnCall project is [rough][rough] consensus. This means that any decision on technical issues is considered supported by the [team][team] as long as nobody objects or the objection has been considered but not necessarily accommodated. -Silence on any consensus decision is implicit agreement and equivalent to explicit agreement. Explicit agreement may be stated at will. Decisions may, but do not need to be called out and put up for decision on the [developers mailing list][devs] at any time and by anyone. +Silence on any consensus decision is implicit agreement and equivalent to explicit agreement. Explicit agreement may be stated at will. Decisions may, but do not need to be called out and put up for decision on the [GitHub Discussions][https://github.com/grafana/oncall/discussions] at any time and by anyone. Consensus decisions can never override or go against the spirit of an earlier explicit vote. @@ -140,7 +142,7 @@ If there are multiple alternatives, members may vote for one or more alternative The new member is - added to the list of [team members](#team-members). Ideally by sending a PR of their own, at least approving said PR. -- announced on the [developers mailing list][devs] by an existing team member. Ideally, the new member replies in this thread, acknowledging team membership. +- announced on the [GitHub Discussions][https://github.com/grafana/oncall/discussions] by an existing team member. Ideally, the new member replies in this thread, acknowledging team membership. - added to the projects with commit rights. - added to the [team mailing list][team]. @@ -155,10 +157,3 @@ The ex-member is - added to a list of previous members if they so choose. If needed, we reserve the right to publicly announce removal. - -[announce]: TODO -[coc]: https://github.com/grafana/oncall/blob/master/CODE_OF_CONDUCT.md -[devs]: TODO -[maintainers]: https://github.com/grafana/oncall/blob/master/MAINTAINERS.md -[rough]: https://tools.ietf.org/html/rfc7282 -[team]: TODO diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 97c9ba33..bd9b78f3 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,12 +1,11 @@ The following are the main/default maintainers: - Ildar Iskhakov — [@iskhakov](https://github.com/iskhakov) ([Grafana Labs](https://grafana.com/)) -- Matvey Kuku — [@Matvey-Kuk](https://github.com/Matvey-Kuk) ([Grafana Labs](https://grafana.com/)) +- Matvey Kukuy — [@Matvey-Kuk](https://github.com/Matvey-Kuk) ([Grafana Labs](https://grafana.com/)) Some parts of the codebase have other maintainers, the package paths also include all sub-packages: -- `docs`: - - Eve Meelan — [@Eve832](https://github.com/Eve832) ([Grafana Labs](https://grafana.com/)) +n/a For the sake of brevity, not all subtrees are explicitly listed. Due to the size of this repository, the natural changes in focus of maintainers over time, From 4d4bbd7297350a99495eb1fd3053e36e2916f3e8 Mon Sep 17 00:00:00 2001 From: Matvey Kukuy Date: Tue, 7 Jun 2022 02:03:14 +0300 Subject: [PATCH 08/14] Small polishing, links for OSS config page --- DEVELOPER.md | 4 +- grafana-plugin/src/GrafanaPluginRootPage.tsx | 2 +- .../PluginConfigPage/PluginConfigPage.tsx | 53 +++++++++---------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/DEVELOPER.md b/DEVELOPER.md index fd4da888..17d00475 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -59,7 +59,7 @@ mkdir sqlite_data # Migrate the DB: python manage.py migrate -# Create user for django admin panel: +# Create user for django admin panel (if you need it): python manage.py createsuperuser ``` @@ -69,7 +69,7 @@ python manage.py createsuperuser # Http server: python manage.py runserver -# Worker for background tasks(run it in the parallel terminal, don't forget to export .env there) +# Worker for background tasks (run it in the parallel terminal, don't forget to export .env there) python manage.py start_celery # Additionally you could launch the worker with periodic tasks launcher (99% you don't need this) diff --git a/grafana-plugin/src/GrafanaPluginRootPage.tsx b/grafana-plugin/src/GrafanaPluginRootPage.tsx index a3276a5f..5eb10a1f 100644 --- a/grafana-plugin/src/GrafanaPluginRootPage.tsx +++ b/grafana-plugin/src/GrafanaPluginRootPage.tsx @@ -48,7 +48,7 @@ const RootWithLoader = observer((props: AppRootProps) => { } else if (store.isUserAnonymous) { text = '😞 Unfortunately Grafana OnCall is available for authorized users only, please sign in to proceed.'; } else if (store.retrySync) { - text = `🚫 OnCall took too many tries to synchronize`; + text = `🚫 OnCall took too many tries to synchronize... Are background workers up and running?`; } return ( diff --git a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx index e3ccfc4c..15fe0581 100644 --- a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx +++ b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx @@ -189,7 +189,9 @@ export const PluginConfigPage = (props: Props) => { if (counter >= 5) { clearInterval(interval); - setPluginStatusMessage(`OnCall took too many tries to synchronize.`); + setPluginStatusMessage( + `OnCall took too many tries to synchronize. Did you launch Celery workers? Background workers should perform synchronization, not web server.` + ); setRetrySync(true); setPluginStatusOk(false); setPluginConfigLoading(false); @@ -212,7 +214,7 @@ export const PluginConfigPage = (props: Props) => { Configure Grafana OnCall {pluginStatusOk && (

- Configuration was sucessfully created. Now you can find Grafana OnCall on right toolbar.{' '} + Configuration was successfully created. Now you can find Grafana OnCall on right toolbar.{' '} Grafana OnCall Logo

)} @@ -242,12 +244,28 @@ export const PluginConfigPage = (props: Props) => { Configure Grafana OnCall

This page will help you to connect OnCall backend and OnCall Grafana plugin 👋

-

1. Grafana OnCall is a Grafana plugin and backend. Run backend

+

+ + - Talk to the OnCall team in the #grafana-oncall channel at{' '} + + Slack + +
- Ask questions at{' '} + + GitHub Discussions + {' '} + or file bugs at{' '} + + GitHub Issues + +
+

+

1. Launch backend

Run production backend using{' '} - - this instructions at our GitHub + + this instructions at our GitHub , @@ -267,27 +285,6 @@ export const PluginConfigPage = (props: Props) => { - - - Need help? -
- 1. Talk to the developers in the #grafana-oncall channel at{' '} - - Slack - -
- 2. Search for issues or create a new one in the{' '} - - GitHub - -
- - } - />

2. Conect the backend and the plugin

{'Plugin <-> backend connection status'}

@@ -301,7 +298,7 @@ Seek for such a line: “Your invite token: <> , use it in the Graf > <> - + How to re-issue the invite token? @@ -311,7 +308,7 @@ Seek for such a line: “Your invite token: <> , use it in the Graf Date: Tue, 7 Jun 2022 10:07:36 -0600 Subject: [PATCH 09/14] Remove branch name from temp plugin file to avoid incompatible characters (#19) Co-authored-by: Michael Derynck --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 4fc14748..687dd3e6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -30,8 +30,8 @@ steps: - yarn ci-build:finish - yarn ci-package - cd ci/dist - - zip -r grafana-oncall-app-${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}.zip ./grafana-oncall-app - - if [ -z "$DRONE_TAG" ]; then echo "No tag, skipping archive"; else cp grafana-oncall-app-${DRONE_BRANCH}-${DRONE_BUILD_NUMBER}.zip grafana-oncall-app-${DRONE_TAG}.zip; fi + - zip -r grafana-oncall-app.zip ./grafana-oncall-app + - if [ -z "$DRONE_TAG" ]; then echo "No tag, skipping archive"; else cp grafana-oncall-app.zip grafana-oncall-app-${DRONE_TAG}.zip; fi - name: Publish Plugin to GCS (release) image: plugins/gcs From 591db52ab82dbd8da3bca4a6a0c2cf78e37d9d5f Mon Sep 17 00:00:00 2001 From: Jack Baldry Date: Tue, 7 Jun 2022 16:45:49 +0100 Subject: [PATCH 10/14] Support whitespace in mount source directory Signed-off-by: Jack Baldry --- docs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index f66259da..5ddacacf 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -8,4 +8,4 @@ pull: .PHONY: docs docs: pull - docker run -v $(shell pwd)/sources:$(CONTENT_PATH):Z -p $(PORT) --rm -it $(IMAGE) + docker run -v '$(shell pwd)/sources:$(CONTENT_PATH):Z' -p $(PORT) --rm -it $(IMAGE) From 72332eddfc0200401f8703d2cc94c03aa6e793fe Mon Sep 17 00:00:00 2001 From: Ildar Iskhakov Date: Wed, 8 Jun 2022 11:11:50 +0300 Subject: [PATCH 11/14] Add pymysql, bump django-mysql version --- engine/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/requirements.txt b/engine/requirements.txt index a9dfc03d..1aaf78c5 100644 --- a/engine/requirements.txt +++ b/engine/requirements.txt @@ -9,7 +9,6 @@ celery==4.3.0 redis==3.2.0 django-celery-results==1.0.4 humanize==0.5.1 -django-mysql==2.4.1 uwsgi==2.0.20 django-cors-headers==3.7.0 django-debug-toolbar==3.2.1 @@ -39,3 +38,5 @@ django-rest-polymorphic==0.1.9 pre-commit==2.15.0 https://github.com/iskhakov/django-push-notifications/archive/refs/tags/2.0.0-hotfix-4.tar.gz django-mirage-field==1.3.0 +django-mysql==4.6.0 +PyMySQL==1.0.2 From 6c6848d66ddeeac08de2862aec536ae25cbc2000 Mon Sep 17 00:00:00 2001 From: Matvey Kukuy Date: Wed, 8 Jun 2022 15:33:50 +0300 Subject: [PATCH 12/14] Logo --- grafana-plugin/src/img/logo.svg | 4 ++-- grafana-plugin/src/utils/consts.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/grafana-plugin/src/img/logo.svg b/grafana-plugin/src/img/logo.svg index 7029f330..7c0277d9 100644 --- a/grafana-plugin/src/img/logo.svg +++ b/grafana-plugin/src/img/logo.svg @@ -1,7 +1,7 @@ - + - + diff --git a/grafana-plugin/src/utils/consts.ts b/grafana-plugin/src/utils/consts.ts index 7546075e..c5e77b2a 100644 --- a/grafana-plugin/src/utils/consts.ts +++ b/grafana-plugin/src/utils/consts.ts @@ -1,4 +1,4 @@ import plugin from '../../package.json'; // eslint-disable-line export const APP_TITLE = 'Grafana OnCall'; -export const APP_SUBTITLE = `Incident Response powered by Amixr (${plugin?.version})`; +export const APP_SUBTITLE = `Incident Response (${plugin?.version})`; From 52587b74f07518fa2be94b8f294b02c2a76e78a7 Mon Sep 17 00:00:00 2001 From: Matvey Kukuy Date: Wed, 8 Jun 2022 23:48:09 +0300 Subject: [PATCH 13/14] Texts update on the plugin configuration page --- .../src/containers/PluginConfigPage/PluginConfigPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx index 15fe0581..b83170a4 100644 --- a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx +++ b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx @@ -214,14 +214,14 @@ export const PluginConfigPage = (props: Props) => { Configure Grafana OnCall {pluginStatusOk && (

- Configuration was successfully created. Now you can find Grafana OnCall on right toolbar.{' '} + Plugin and the backend are connected! Check Grafana OnCall 👈👈👈{' '} Grafana OnCall Logo

)} {isSelfHostedInstall ? (
-

{'Plugin <-> backend connection status'}

+

{'Plugin <-> backend connection status:'}

                 {pluginStatusMessage}
               
@@ -287,7 +287,7 @@ export const PluginConfigPage = (props: Props) => {

2. Conect the backend and the plugin

-

{'Plugin <-> backend connection status'}

+

{'Plugin <-> backend connection status:'}

             {pluginStatusMessage}
           
From e6ac28a21a458a53f724929676bd8cfc68c78d03 Mon Sep 17 00:00:00 2001 From: Michael Derynck Date: Wed, 8 Jun 2022 17:05:18 -0600 Subject: [PATCH 14/14] Update grafana version (#27) * Update grafana version * Testing drone project protection * Add signature to drone file * Add drone signing instructions Co-authored-by: Michael Derynck --- .drone.yml | 8 +++++++- DEVELOPER.md | 15 +++++++++++++++ developer-docker-compose.yml | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 687dd3e6..9157d8ee 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,3 +1,4 @@ +--- kind: pipeline type: docker name: Build and Release @@ -227,4 +228,9 @@ get: name: machine-user-token path: infra/data/ci/drone kind: secret -name: drone_token \ No newline at end of file +name: drone_token +--- +kind: signature +hmac: 81b9b7cda5a8f8525f40f39821be50881e0c4cb0c40a45b3e63bc0cc47274649 + +... diff --git a/DEVELOPER.md b/DEVELOPER.md index 17d00475..75b32da0 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -383,3 +383,18 @@ pytest --ds=settings.dev - Set Settings to settings/dev.py 5. Create a new Django Server run configuration to Run/Debug the engine - Use a plugin such as EnvFile to load the .env file + +## Update drone build +The .drone.yml build file must be signed when changes are made to it. Follow these steps: + +If you have not installed drone CLI follow [these instructions](https://docs.drone.io/cli/install/) + +To sign the .drone.yml file: +```bash +export DRONE_SERVER=https://drone.grafana.net + +# Get your drone token from https://drone.grafana.net/account +export DRONE_TOKEN= + +drone sign --save grafana/oncall .drone.yml +``` diff --git a/developer-docker-compose.yml b/developer-docker-compose.yml index b24312d6..08ce52fc 100644 --- a/developer-docker-compose.yml +++ b/developer-docker-compose.yml @@ -48,7 +48,7 @@ services: condition: service_healthy grafana: - image: "grafana/grafana:8.3.2" + image: "grafana/grafana:8.5.5" restart: always mem_limit: 500m cpus: 0.5