Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement Pagerduty #44

Merged
merged 1 commit into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const alertRecipientLabelMap: any = {
http: 'HTTP',
oc: 'OC',
slack: 'Slack',
opsgenie: 'OpsGenie'
opsgenie: 'OpsGenie',
pagerduty: 'PagerDuty'
};

@Component({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,15 @@
</mat-form-field>
</div>
</div>
<div class="pager-duty-specific-fields" *ngIf="notificationRecipientsValue && notificationRecipientsValue.pagerduty">
<div class="form-group alert-pagerduty-autoclose">
<div class="form-label vertical-center"><strong>PagerDuty AutoClose</strong></div>
<div class="pagerduty-autoclose-flag">
<mat-checkbox formControlName="pagerdutyAutoClose" value="true"></mat-checkbox>
<mat-hint class="info-hint">Enabling AutoClose will ignore `Send one notification per` setting for PagerDuty. Alerts will be sent per unique tag set.</mat-hint>
</div>
</div>
</div>
</div>
</div>

Expand Down Expand Up @@ -939,6 +948,15 @@
</mat-form-field>
</div>
</div>
<div class="pager-duty-specific-fields" *ngIf="notificationRecipientsValue && notificationRecipientsValue.pagerduty">
<div class="form-group alert-pagerduty-autoclose">
<div class="form-label vertical-center"><strong>PagerDuty AutoClose</strong></div>
<div class="pagerduty-autoclose-flag">
<mat-checkbox formControlName="pagerdutyAutoClose" value="true"></mat-checkbox>
<mat-hint class="info-hint">Enabling AutoClose will ignore `Send one notification per` setting for PagerDuty. Alerts will be sent per unique tag set.</mat-hint>
</div>
</div>
</div>
</div>
</div>

Expand Down Expand Up @@ -1194,6 +1212,15 @@
</div>
</div>
</div>
<div class="pager-duty-specific-fields" *ngIf="notificationRecipientsValue && notificationRecipientsValue.pagerduty">
<div class="form-group alert-pagerduty-autoclose">
<div class="form-label vertical-center"><strong>PagerDuty AutoClose</strong></div>
<div class="pagerduty-autoclose-flag">
<span>{{ data.notification.pagerdutyAutoClose === true ? 'Yes' : 'No' }}</span>
<mat-hint class="info-hint" *ngIf="data.notification.pagerdutyAutoClose === true">Enabling AutoClose will ignore `Send one notification per` setting for Paferduty. Alerts will be sent per unique tag set.</mat-hint>
</div>
</div>
</div>
</div>
</div>
<div class="step-section misc-details is-visible" *ngIf="data.id">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,9 @@ export class AlertDetailsComponent implements OnInit, OnDestroy, AfterContentIni
// OC conditional values
runbookId: data.notification.runbookId || '',
ocSeverity: data.notification.ocSeverity || this.defaultOCSeverity,
ocTier: data.notification.ocTier || this.defaultOCTier
ocTier: data.notification.ocTier || this.defaultOCTier,
// PagerDuty conditional values
pagerdutyAutoClose: data.notification.pagerdutyAutoClose || false
})
});
this.prevTimeSampler = data.threshold.singleMetric.timeSampler || 'at_least_once';
Expand Down Expand Up @@ -754,7 +756,9 @@ export class AlertDetailsComponent implements OnInit, OnDestroy, AfterContentIni
// OC conditional values
runbookId: data.notification.runbookId || '',
ocSeverity: data.notification.ocSeverity || this.defaultOCSeverity,
ocTier: data.notification.ocTier || this.defaultOCTier
ocTier: data.notification.ocTier || this.defaultOCTier,
// PagerDuty conditional values
pagerdutyAutoClose: data.notification.pagerdutyAutoClose || false
})
});
this.setTags();
Expand Down Expand Up @@ -834,7 +838,9 @@ export class AlertDetailsComponent implements OnInit, OnDestroy, AfterContentIni
opsgenieTags: this.fb.array(data.notification.opsgenieTags || []),
runbookId: data.notification.runbookId || '',
ocSeverity: data.notification.ocSeverity || this.defaultOCSeverity,
ocTier: data.notification.ocTier || this.defaultOCTier
ocTier: data.notification.ocTier || this.defaultOCTier,
// PagerDuty conditional values
pagerdutyAutoClose: data.notification.pagerdutyAutoClose || false
})
});
this.options.axes.y.valueRange[0] = 0;
Expand Down Expand Up @@ -2009,6 +2015,10 @@ export class AlertDetailsComponent implements OnInit, OnDestroy, AfterContentIni
this.alertForm['controls'].notification.get('opsgenieAutoClose').setValue(false);
this.alertForm.get('notification')['controls']['opsgenieTags'] = this.fb.array([]);
}

if (this.notificationRecipients.value.pagerduty && !event.pagerduty) {
this.alertForm['controls'].notification.get('pagerdutyAutoClose').setValue(false);
}
this.notificationRecipients.setValue(event);

}
Expand Down Expand Up @@ -2092,7 +2102,8 @@ export class AlertDetailsComponent implements OnInit, OnDestroy, AfterContentIni
slack: 'Slack',
http: 'Webhook',
oc: 'OC',
email: 'Email'
email: 'Email',
pagerduty: 'PagerDuty'
}
return types[type];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export enum RecipientType {
slack = 'slack',
http = 'http',
oc = 'oc',
email = 'email'
email = 'email',
pagerduty = 'pagerduty'
}

export class Recipient {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,38 @@
</mat-form-field>
</div>
</div>

<!--Pager duty Form-->
<div *ngIf="recipientType === _recipientType.pagerduty" class="contacts-create-form">
<div class="form-group">
<div class="form-label vertical-center">Team Name</div>
<mat-form-field floatLabel="never" appearance="fill">
<input matInput [formControl]="pagerDutyName" placeholder="Enter Team Name*" required
minlength="2" maxlength="36" [value]="recipientsFormData[_recipientType.pagerduty].name"
(input)="updateRecipient(recipientsFormData[_recipientType.pagerduty], 'name', $event.target.value)">
<mat-error *ngIf="pagerDutyName.errors && (pagerDutyName.dirty || pagerDutyName.touched)">
Unique Team Name required. Min length is 2. Max length is 36.
</mat-error>
</mat-form-field>
</div>
<div class="form-group">
<div class="form-label vertical-center">Routing Key</div>
<mat-form-field floatLabel="never" appearance="fill" class="wide-formfield">
<input matInput [formControl]="pagerDutyRoutingKey" placeholder="Enter Routing Key*" required
minlength="32" maxlength="32" [value]="recipientsFormData[_recipientType.pagerduty].routingkey"
(input)="updateRecipient(recipientsFormData[_recipientType.pagerduty], 'routingkey', $event.target.value)">
<mat-error *ngIf="pagerDutyRoutingKey.errors && (pagerDutyRoutingKey.dirty || pagerDutyRoutingKey.touched)">
Routing Key required. Length is {{pagerDutyRoutingKeyMaxLength}}.
</mat-error>
</mat-form-field>
</div>
<div class="form-group">
<div class="form-label"></div>
<span>
Follow steps outlined in <a href="{{config.alert.recipient.pagerduty.guideUrl}}" target="_blank">the user-guide</a> to create an API key.
</span>
</div>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
];
slackWebhookMaxLength = 200;
opsGenieApiKeyMaxLength = 200;
pagerDutyRoutingKeyMaxLength = 32;

_mode = Mode; // for template
_recipientType = RecipientType; // for template
Expand All @@ -82,6 +83,8 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
httpName = new FormControl('');
httpEndpoint = new FormControl('');
emailAddress = new FormControl('');
pagerDutyName = new FormControl('');
pagerDutyRoutingKey = new FormControl('');

// state control
private nsRecipientSub: Subscription;
Expand Down Expand Up @@ -112,6 +115,10 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
if (this.emailAddress.errors) {
return true;
}
} else if (this.recipientType === RecipientType.pagerduty) {
if (this.pagerDutyName.errors || this.pagerDutyRoutingKey.errors) {
return true;
}
}
return false;
}
Expand Down Expand Up @@ -291,6 +298,8 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
this.httpName.setValue(this.recipientsFormData[RecipientType.http].name);
this.httpEndpoint.setValue(this.recipientsFormData[RecipientType.http].endpoint);
this.emailAddress.setValue(this.recipientsFormData[RecipientType.email].name);
this.pagerDutyName.setValue(this.recipientsFormData[RecipientType.pagerduty].name);
this.pagerDutyRoutingKey.setValue(this.recipientsFormData[RecipientType.pagerduty].routingkey);
}

addUserInputToAlertRecipients($event: MatChipInputEvent) {
Expand Down Expand Up @@ -472,6 +481,8 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
return 'OC';
} else if (type === RecipientType.email) {
return 'Email';
} else if (type === RecipientType.pagerduty) {
return 'PagerDuty';
}
return '';
}
Expand All @@ -483,6 +494,7 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
let emptyHTTPRecipient = this.createDefaultRecipient(RecipientType.http);
let emptyOCRecipient = this.createDefaultRecipient(RecipientType.oc);
let emptyEmailRecipient = this.createDefaultRecipient(RecipientType.email);
let emptyPagerDutyRecipient = this.createDefaultRecipient(RecipientType.pagerduty);

// Set Defaults
emptyOpsGenieRecipient.apikey = '';
Expand All @@ -492,12 +504,14 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
emptyOCRecipient.context = 'analysis';
emptyOCRecipient.opsdbproperty = '';
emptyEmailRecipient.name = '';
emptyPagerDutyRecipient.routingkey = '';

emptyRecipients[RecipientType.opsgenie] = emptyOpsGenieRecipient;
emptyRecipients[RecipientType.slack] = emptySlackRecipient;
emptyRecipients[RecipientType.http] = emptyHTTPRecipient;
emptyRecipients[RecipientType.oc] = emptyOCRecipient;
emptyRecipients[RecipientType.email] = emptyEmailRecipient;
emptyRecipients[RecipientType.pagerduty] = emptyPagerDutyRecipient;
this.recipientsFormData = emptyRecipients;
}

Expand Down Expand Up @@ -533,6 +547,10 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
return apiKey && apiKey.length > 0 && apiKey.length <= this.opsGenieApiKeyMaxLength;
}

isPagerDutyRoutingKeyCorrectLength(routingkey: string): boolean {
return routingkey && routingkey.length > 0 && routingkey.length <= this.pagerDutyRoutingKeyMaxLength;
}

getRecipientItemsByType(type) {
if (this.viewMode === Mode.all) {
// all mode (show only unselected)
Expand Down Expand Up @@ -618,6 +636,13 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
};
}

pagerDutyRoutingKeyValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
let forbidden = !this.isPagerDutyRoutingKeyCorrectLength(control.value);
return forbidden ? {'forbiddenName': {value: control.value}} : null;
};
}

urlValidator() : ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
let forbidden = !/^https:\/\/(www\.)?(([-a-zA-Z0-9@:%._[\]]{1,256}\.[a-zA-Z0-9()]{0,6}\b)|(\[?[a-fA-F0-9]*:[a-fA-F0-9:]+\]))([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/.test(control.value);
Expand All @@ -635,6 +660,8 @@ export class AlertConfigurationContactsComponent implements OnInit, OnChanges, O
this.ocName = new FormControl('', [this.forbiddenNameValidator(this.getAllRecipientsForType(RecipientType.oc), this.recipientsFormData[this.recipientType])]);
this.httpName = new FormControl('', [this.forbiddenNameValidator(this.getAllRecipientsForType(RecipientType.http), this.recipientsFormData[this.recipientType])]);
this.emailAddress = new FormControl('', [this.forbiddenNameValidator(this.getAllRecipientsForType(RecipientType.email), this.recipientsFormData[this.recipientType]), this.emailValidator()]);
this.pagerDutyName = new FormControl('', [this.forbiddenNameValidator(this.getAllRecipientsForType(RecipientType.pagerduty), this.recipientsFormData[this.recipientType])]);
this.pagerDutyRoutingKey = new FormControl('', [this.pagerDutyRoutingKeyValidator()]);
}

trimRecipientName(name) {
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/app/alerts/containers/alerts.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export class AlertsComponent implements OnInit, OnDestroy, AfterViewChecked {
this.sparklineDisplay = this.sparklineDisplayMenuOptions[0];

// icons
const svgIcons = ['email', 'http', 'oc', 'opsgenie', 'slack'];
const svgIcons = ['email', 'http', 'oc', 'opsgenie', 'slack', 'pagerduty'];

}

Expand Down Expand Up @@ -1376,6 +1376,8 @@ export class AlertsComponent implements OnInit, OnDestroy, AfterViewChecked {
return 'OC';
} else if (type === RecipientType.email) {
return 'Email';
} else if (type === RecipientType.pagerduty) {
return 'PagerDuty';
}
return '';
}
Expand Down
6 changes: 5 additions & 1 deletion server/config/app_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@
},
"oc": {
"enable": false,
"onboardUrl": "<% OC onboad url %>",
"onboardUrl": "<% OC onboard url %>",
"guideUrl": "<% OC guide url %>"
},
"http": {
"enable": false
},
"email": {
"enable": true
},
"pagerduty": {
"enable": false,
"guideUrl": "<% pagerduty guide url %>"
}
}
},
Expand Down