Skip to content

Commit 1f3edd6

Browse files
authored
v1.9.27
2 parents d82ca01 + e882e97 commit 1f3edd6

File tree

28 files changed

+303
-178
lines changed

28 files changed

+303
-178
lines changed

.github/actions/build-sign-and-package-plugin/action.yml

+24-18
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,35 @@ name: Build, sign, and package plugin
22
description: Build, sign, and package plugin
33
inputs:
44
plugin_version_number:
5-
description: "The version number of the plugin"
5+
description: |
6+
The version number of the plugin. NOTE: this action will chop off the leading "v" to use
7+
it as the official plugin version.
68
required: true
7-
grafana_access_policy_token:
8-
description: "The Grafana access policy token used to sign the plugin"
9-
required: true
10-
working_directory:
11-
description: "The working directory of the plugin"
12-
required: true
13-
is_enterprise:
14-
description: "Whether the plugin is an enterprise build or not"
15-
required: false
16-
default: "false"
179
outputs:
1810
artifact_filename:
1911
description: "The filename of the plugin artifact"
2012
value: ${{ steps.artifact-filename.outputs.filename }}
2113
runs:
2214
using: "composite"
2315
steps:
16+
# This will fetch the secret keys from vault and set them as environment variables for subsequent steps
17+
- name: Get Vault secrets
18+
uses: grafana/shared-workflows/actions/get-vault-secrets@main
19+
with:
20+
repo_secrets: |
21+
GRAFANA_ACCESS_POLICY_TOKEN=github_actions:cloud-access-policy-token
22+
- name: Determine official plugin version
23+
id: plugin-version
24+
shell: bash
25+
run: |
26+
# VERY IMPORTANT: chop off the "v".. this tells the oncall plugin that this is an OSS build
27+
PLUGIN_VERSION="$(echo ${{ inputs.plugin_version_number }} | sed 's/^v//')"
28+
echo version="$PLUGIN_VERSION" >> $GITHUB_OUTPUT
2429
- name: Determine artifact filename
2530
shell: bash
2631
id: artifact-filename
27-
# yamllint disable rule:line-length
2832
run: |
29-
echo filename="grafana-oncall${{ inputs.is_enterprise == 'true' && '-ee' || '' }}-app-${{ inputs.plugin_version_number }}.zip" >> $GITHUB_OUTPUT
33+
echo filename="grafana-oncall-app-${{ steps.plugin-version.outputs.version }}.zip" >> $GITHUB_OUTPUT
3034
- name: Install Go
3135
uses: actions/setup-go@v4
3236
with:
@@ -36,16 +40,18 @@ runs:
3640
run: go install github.com/magefile/[email protected]
3741
- name: Build, sign, and package plugin
3842
shell: bash
39-
working-directory: ${{ inputs.working_directory }}
40-
env:
41-
GRAFANA_ACCESS_POLICY_TOKEN: ${{ inputs.grafana_access_policy_token }}
43+
working-directory: grafana-plugin
4244
run: |
43-
jq --arg v "${{ inputs.plugin_version_number }}" '.version=$v' package.json > package.new && mv package.new package.json && jq '.version' package.json;
45+
jq --arg v "${{ steps.plugin-version.outputs.version }}" '.version=$v' package.json > package.new && \
46+
mv package.new package.json && \
47+
jq '.version' package.json;
48+
4449
pnpm build
4550
mage buildAll || true
51+
4652
pnpm sign
53+
4754
if [ ! -f dist/MANIFEST.txt ]; then echo "Sign failed, MANIFEST.txt not created, aborting." && exit 1; fi
4855
mv dist grafana-oncall-app
4956
zip -r grafana-oncall-app.zip ./grafana-oncall-app
5057
cp grafana-oncall-app.zip ${{ steps.artifact-filename.outputs.filename }}
51-
# yamllint enable rule:line-length

.github/workflows/on-release-published.yml

-3
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,12 @@ jobs:
3636
uses: grafana/shared-workflows/actions/get-vault-secrets@main
3737
with:
3838
repo_secrets: |
39-
GRAFANA_ACCESS_POLICY_TOKEN=github_actions:cloud-access-policy-token
4039
GCS_PLUGIN_PUBLISHER_SERVICE_ACCOUNT_JSON=github_actions:gcs-plugin-publisher
4140
- name: Build, sign, and package plugin
4241
id: build-sign-and-package-plugin
4342
uses: ./.github/actions/build-sign-and-package-plugin
4443
with:
4544
plugin_version_number: ${{ github.ref_name }}
46-
grafana_access_policy_token: ${{ env.GRAFANA_ACCESS_POLICY_TOKEN }}
47-
working_directory: grafana-plugin
4845
- name: Authenticate with GCS
4946
uses: google-github-actions/auth@v2
5047
with:

.pre-commit-config.yaml

+14-14
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,20 @@ repos:
6464
- "@grafana/eslint-config@^5.0.0"
6565
- eslint-plugin-promise@^6.1.1
6666

67-
- repo: https://github.com/pre-commit/mirrors-prettier
68-
rev: "v2.7.1"
69-
hooks:
70-
- id: prettier
71-
name: prettier
72-
types_or: [css, javascript, jsx, ts, tsx]
73-
files: ^grafana-plugin/src
74-
additional_dependencies:
75-
76-
- id: prettier
77-
name: prettier - json
78-
types_or: [json]
79-
additional_dependencies:
80-
67+
# - repo: https://github.com/pre-commit/mirrors-prettier
68+
# rev: "v2.7.1"
69+
# hooks:
70+
# - id: prettier
71+
# name: prettier
72+
# types_or: [css, javascript, jsx, ts, tsx]
73+
# files: ^grafana-plugin/src
74+
# additional_dependencies:
75+
76+
# - id: prettier
77+
# name: prettier - json
78+
# types_or: [json]
79+
# additional_dependencies:
80+
8181

8282
- repo: https://github.com/thibaudcolas/pre-commit-stylelint
8383
rev: v13.13.1

docs/sources/oncall-api-reference/alertgroups.md

+32
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,38 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unresolve" \
130130

131131
`POST {{API_URL}}/api/v1/alert_groups/<ALERT_GROUP_ID>/unresolve`
132132

133+
## Silence an alert group
134+
135+
```shell
136+
curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/silence" \
137+
--request POST \
138+
--header "Authorization: meowmeowmeow" \
139+
--header "Content-Type: application/json" \
140+
--data '{
141+
"delay": 10800
142+
}'
143+
```
144+
145+
**HTTP request**
146+
147+
`POST {{API_URL}}/api/v1/alert_groups/<ALERT_GROUP_ID>/silence`
148+
149+
| Parameter | Required | Description |
150+
|-----------|:--------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
151+
| `delay` | Yes | The duration of silence in seconds, `-1` for silencing the alert forever |
152+
153+
## Unsilence an alert group
154+
155+
```shell
156+
curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unsilence" \
157+
--request POST \
158+
--header "Authorization: meowmeowmeow"
159+
```
160+
161+
**HTTP request**
162+
163+
`POST {{API_URL}}/api/v1/alert_groups/<ALERT_GROUP_ID>/unsilence`
164+
133165
## Delete an alert group
134166

135167
```shell

engine/apps/public_api/tests/test_alert_groups.py

+95
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,98 @@ def test_alert_group_unresolve(
635635
alert_group.refresh_from_db()
636636
assert alert_group.resolved is False
637637
assert alert_group.log_records.last().action_source == ActionSource.API
638+
639+
640+
@pytest.mark.parametrize(
641+
"acknowledged,resolved,attached,status_code,data,response_msg",
642+
[
643+
(False, False, False, status.HTTP_200_OK, {"delay": 60}, None),
644+
(False, False, False, status.HTTP_400_BAD_REQUEST, {"delay": -2}, "invalid delay value"),
645+
(False, False, False, status.HTTP_400_BAD_REQUEST, {"delay": "fuzz"}, "invalid delay value"),
646+
(False, False, False, status.HTTP_400_BAD_REQUEST, {}, "delay is required"),
647+
(True, False, False, status.HTTP_400_BAD_REQUEST, {"delay": 60}, "Can't silence an acknowledged alert group"),
648+
(False, True, False, status.HTTP_400_BAD_REQUEST, {"delay": 60}, "Can't silence a resolved alert group"),
649+
(False, False, True, status.HTTP_400_BAD_REQUEST, {"delay": 60}, "Can't silence an attached alert group"),
650+
],
651+
)
652+
@pytest.mark.django_db
653+
def test_alert_group_silence(
654+
make_organization_and_user_with_token,
655+
make_alert_receive_channel,
656+
make_alert_group,
657+
acknowledged,
658+
resolved,
659+
attached,
660+
status_code,
661+
data,
662+
response_msg,
663+
):
664+
organization, _, token = make_organization_and_user_with_token()
665+
alert_receive_channel = make_alert_receive_channel(organization)
666+
root_alert_group = make_alert_group(alert_receive_channel)
667+
alert_group = make_alert_group(
668+
alert_receive_channel,
669+
acknowledged=acknowledged,
670+
resolved=resolved,
671+
root_alert_group=root_alert_group if attached else None,
672+
)
673+
674+
client = APIClient()
675+
url = reverse("api-public:alert_groups-silence", kwargs={"pk": alert_group.public_primary_key})
676+
response = client.post(url, data=data, HTTP_AUTHORIZATION=token)
677+
678+
if status_code == status.HTTP_200_OK:
679+
alert_group.refresh_from_db()
680+
assert alert_group.silenced is True
681+
assert alert_group.log_records.last().action_source == ActionSource.API
682+
else:
683+
assert alert_group.silenced is False
684+
assert response.status_code == status_code
685+
assert response_msg == response.json()["detail"]
686+
687+
688+
@pytest.mark.parametrize(
689+
"silenced,resolved,acknowledged,attached,status_code,response_msg",
690+
[
691+
(True, False, False, False, status.HTTP_200_OK, None),
692+
(False, False, False, False, status.HTTP_400_BAD_REQUEST, "Can't unsilence an unsilenced alert group"),
693+
(True, True, False, False, status.HTTP_400_BAD_REQUEST, "Can't unsilence a resolved alert group"),
694+
(True, False, True, False, status.HTTP_400_BAD_REQUEST, "Can't unsilence an acknowledged alert group"),
695+
(True, False, False, True, status.HTTP_400_BAD_REQUEST, "Can't unsilence an attached alert group"),
696+
],
697+
)
698+
@pytest.mark.django_db
699+
def test_alert_group_unsilence(
700+
make_organization_and_user_with_token,
701+
make_alert_receive_channel,
702+
make_alert_group,
703+
silenced,
704+
resolved,
705+
acknowledged,
706+
attached,
707+
status_code,
708+
response_msg,
709+
):
710+
organization, _, token = make_organization_and_user_with_token()
711+
alert_receive_channel = make_alert_receive_channel(organization)
712+
root_alert_group = make_alert_group(alert_receive_channel)
713+
alert_group = make_alert_group(
714+
alert_receive_channel,
715+
acknowledged=acknowledged,
716+
resolved=resolved,
717+
silenced=silenced,
718+
root_alert_group=root_alert_group if attached else None,
719+
)
720+
721+
client = APIClient()
722+
url = reverse("api-public:alert_groups-unsilence", kwargs={"pk": alert_group.public_primary_key})
723+
response = client.post(url, HTTP_AUTHORIZATION=token)
724+
725+
if status_code == status.HTTP_200_OK:
726+
alert_group.refresh_from_db()
727+
assert alert_group.silenced is False
728+
assert alert_group.log_records.last().action_source == ActionSource.API
729+
else:
730+
assert alert_group.silenced == silenced
731+
assert response.status_code == status_code
732+
assert response_msg == response.json()["detail"]

engine/apps/public_api/views/alert_groups.py

+46
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,49 @@ def unresolve(self, request, pk):
213213

214214
alert_group.un_resolve_by_user_or_backsync(self.request.user, action_source=ActionSource.API)
215215
return Response(status=status.HTTP_200_OK)
216+
217+
@action(methods=["post"], detail=True)
218+
def silence(self, request, pk=None):
219+
alert_group = self.get_object()
220+
221+
delay = request.data.get("delay")
222+
if delay is None:
223+
raise BadRequest(detail="delay is required")
224+
try:
225+
delay = int(delay)
226+
except ValueError:
227+
raise BadRequest(detail="invalid delay value")
228+
if delay < -1:
229+
raise BadRequest(detail="invalid delay value")
230+
231+
if alert_group.resolved:
232+
raise BadRequest(detail="Can't silence a resolved alert group")
233+
234+
if alert_group.acknowledged:
235+
raise BadRequest(detail="Can't silence an acknowledged alert group")
236+
237+
if alert_group.root_alert_group is not None:
238+
raise BadRequest(detail="Can't silence an attached alert group")
239+
240+
alert_group.silence_by_user_or_backsync(request.user, silence_delay=delay, action_source=ActionSource.API)
241+
return Response(status=status.HTTP_200_OK)
242+
243+
@action(methods=["post"], detail=True)
244+
def unsilence(self, request, pk=None):
245+
alert_group = self.get_object()
246+
247+
if not alert_group.silenced:
248+
raise BadRequest(detail="Can't unsilence an unsilenced alert group")
249+
250+
if alert_group.resolved:
251+
raise BadRequest(detail="Can't unsilence a resolved alert group")
252+
253+
if alert_group.acknowledged:
254+
raise BadRequest(detail="Can't unsilence an acknowledged alert group")
255+
256+
if alert_group.root_alert_group is not None:
257+
raise BadRequest(detail="Can't unsilence an attached alert group")
258+
259+
alert_group.un_silence_by_user_or_backsync(request.user, action_source=ActionSource.API)
260+
261+
return Response(status=status.HTTP_200_OK)

grafana-plugin/e2e-tests/schedules/addOverride.test.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dayjs from 'dayjs';
22

33
import { test, expect, Locator } from '../fixtures';
4-
import { MOSCOW_TIMEZONE } from '../utils/constants';
4+
import { isGrafanaVersionGreaterThan, MOSCOW_TIMEZONE } from '../utils/constants';
55
import { clickButton, generateRandomValue } from '../utils/forms';
66
import { setTimezoneInProfile } from '../utils/grafanaProfile';
77
import { createOnCallSchedule, getOverrideFormDateInputs } from '../utils/schedule';
@@ -12,6 +12,12 @@ test('Default dates in override creation modal are set to today', async ({ admin
1212
const onCallScheduleName = generateRandomValue();
1313
await createOnCallSchedule(page, onCallScheduleName, userName);
1414

15+
await page.clock.setFixedTime(new Date().setHours(12, 0, 0, 0));
16+
await page.getByTestId('timezone-select').locator('svg').click();
17+
await (isGrafanaVersionGreaterThan('11.0.0') ? page.getByRole('option') : page.getByLabel('Select option'))
18+
.getByText(/^GMT$/)
19+
.click();
20+
1521
await clickButton({ page, buttonText: 'Add override' });
1622

1723
const overrideFormDateInputs = await getOverrideFormDateInputs(page);

grafana-plugin/e2e-tests/schedules/timezones.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test.skip('dates in schedule are correct according to selected current timezone'
2222
* Always set a fixed time of today's date but at 12:00:00 (noon)
2323
*
2424
* This solves the issue here https://github.com/grafana/oncall/issues/4991
25-
* where we would occasionally see this test flake if it srtated and finished at a different hour
25+
* where we would occasionally see this test flake if it started and finished at a different hour
2626
*
2727
* See playwright docs for more details
2828
* https://playwright.dev/docs/clock

grafana-plugin/e2e-tests/utils/userSettings.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const configureUserNotificationSettings = async (page: Page, notifyBy: No
7070
// select our notification type, first check if we have any already defined, if so, click the
7171
// "Add Notification Step" button
7272
const defaultNotificationSettingsSection = getDefaultNotificationSettingsSectionByTestId(page);
73-
const addNotificationStepText = 'Add Notification Step';
73+
const addNotificationStepText = 'Add notification step';
7474

7575
if (!(await defaultNotificationSettingsSection.locator(`button >> text=${addNotificationStepText}`).isVisible())) {
7676
await clickButton({

grafana-plugin/package.json

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "grafana-oncall-app",
3-
"version": "1.0.0",
3+
"version": "1.9.23",
44
"description": "Grafana OnCall Plugin",
55
"scripts": {
66
"lint": "eslint --ext .js,.jsx,.ts,.tsx --max-warnings=20 ./src ./e2e-tests",
@@ -11,9 +11,8 @@
1111
"build:dev": "NODE_ENV=development webpack -c ./webpack.config.ts --env development",
1212
"labels:link": "pnpm --dir ../../gops-labels/frontend link && pnpm link \"@grafana/labels\" && pnpm --dir ../../gops-labels/frontend watch",
1313
"labels:unlink": "pnpm --dir ../../gops-labels/frontend unlink",
14-
"mage:build-dev": "go mod download && mage -v build:debug",
15-
"mage:watch": "go mod download && mage -v watch",
16-
"mod:download": "go mod download",
14+
"mage:build-dev": "mage -v build:debug",
15+
"mage:watch": "mage -v watch",
1716
"test-utc": "TZ=UTC jest --verbose --testNamePattern '^((?!@london-tz).)*$'",
1817
"test-london-tz": "TZ=Europe/London jest --verbose --testNamePattern '@london-tz'",
1918
"test": "PLUGIN_ID=grafana-oncall-app pnpm test-utc && pnpm test-london-tz",

grafana-plugin/pkg/plugin/settings.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ type OnCallSettingsCache struct {
105105
otherPluginSettingsExpiry time.Time
106106
}
107107

108-
const CLOUD_VERSION_PATTERN = `^(r\d+-v?\d+\.\d+\.\d+|^github-actions-\d+)$`
109-
const OSS_VERSION_PATTERN = `^(v?\d+\.\d+\.\d+|dev-oss)$`
108+
const CLOUD_VERSION_PATTERN = `^(v\d+\.\d+\.\d+|github-actions-[a-zA-Z0-9-]+)$`
109+
const OSS_VERSION_PATTERN = `^(\d+\.\d+\.\d+)$`
110110
const CLOUD_LICENSE_NAME = "Cloud"
111111
const OPEN_SOURCE_LICENSE_NAME = "OpenSource"
112112
const INCIDENT_PLUGIN_ID = "grafana-incident-app"

grafana-plugin/src/containers/AddResponders/parts/NotificationPoliciesSelect/NotificationPoliciesSelect.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ export const NotificationPoliciesSelect: FC<Props> = ({ disabled = false, import
2424
{
2525
value: NotificationPolicyValue.Default,
2626
label: 'Default',
27-
description: 'Use "Default notifications" from users personal settings',
27+
description: 'Use "Default notification rules" from users personal settings',
2828
},
2929
{
3030
value: NotificationPolicyValue.Important,
3131
label: 'Important',
32-
description: 'Use "Important notifications" from users personal settings',
32+
description: 'Use "Important notification rules" from users personal settings',
3333
},
3434
]}
3535
onChange={onChange}

0 commit comments

Comments
 (0)