Skip to content

Commit 14446c9

Browse files
authored
Merge pull request #3 from cruxstack/dev
feat: add support for recovery point events
2 parents 2bc6ee8 + 973d050 commit 14446c9

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

fixtures/samples.json

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,5 +506,79 @@
506506
},
507507
"state": "MODIFIED"
508508
}
509+
},
510+
{
511+
"version": "0",
512+
"id": "ab32977c-378d-2122-e985-fgh4596f0709",
513+
"detail-type": "Recovery Point State Change",
514+
"source": "aws.backup",
515+
"account": "1112233445566",
516+
"time": "2020-07-15T21:39:07Z",
517+
"region": "us-west-2",
518+
"resources": [
519+
"arn:aws:rds:us-west-2:1112233445566:cluster-snapshot:awsbackup:job-4ece7121-d60e-00c2-5c3b-49960142d03b"
520+
],
521+
"detail": {
522+
"backupVaultName": "e6625738-0655-4aa9-bd37-6ec1dd183b15",
523+
"backupVaultArn": "arn:aws:backup:us-west-2:496821122410:backup-vault:e6625738-0655-4aa9-bd37-6ec1dd183b15",
524+
"creationDate": "2020-07-15T21:38:31.152Z",
525+
"iamRoleArn": "arn:aws:iam::1112233445566:role/FullBackupTestRole",
526+
"resourceType": "Aurora",
527+
"resourceArn": "arn:aws:rds:us-west-2:1112233445566:cluster:id",
528+
"status": "DELETED",
529+
"deletedBy": "MANUAL_DELETE",
530+
"isEncrypted": "false",
531+
"storageClass": "WARM",
532+
"initiationDate": "2025-06-15T07:37:15.911Z",
533+
"completionDate": "2020-07-15T21:39:05.689Z",
534+
"createdBy": {
535+
"backupPlanId": "bde0f455-4e24-4668-aeaa-4932a97f5cc5",
536+
"backupPlanArn": "arn:aws:backup:us-west-2:1112233445566:backup-plan:bde0f455-4e24-4668-aeaa-4932a97f5cc5",
537+
"backupPlanVersion": "YTkzNmM0MmUtMWRhNS00Y2RkLThmZGUtNjA5NTc4NGM1YTc5",
538+
"backupPlanRuleId": "1f97bafa-14d6-4f39-94fd-94b51bd6d0d5"
539+
},
540+
"lifecycle": {
541+
"deleteAfterDays": 100
542+
},
543+
"calculatedLifeCycle": {
544+
"deleteAt": "2020-10-23T21:38:31.152Z"
545+
}
546+
}
547+
},
548+
{
549+
"version": "0",
550+
"id": "ab32977c-378d-2122-e985-fgh4596f0709",
551+
"detail-type": "Recovery Point State Change",
552+
"source": "aws.backup",
553+
"account": "1112233445566",
554+
"time": "2020-07-15T21:39:07Z",
555+
"region": "us-west-2",
556+
"resources": [
557+
"arn:aws:rds:us-west-2:1112233445566:cluster-snapshot:awsbackup:job-4ece7121-d60e-00c2-5c3b-49960142d03b"
558+
],
559+
"detail": {
560+
"backupVaultName": "e6625738-0655-4aa9-bd37-6ec1dd183b15",
561+
"backupVaultArn": "arn:aws:backup:us-west-2:496821122410:backup-vault:e6625738-0655-4aa9-bd37-6ec1dd183b15",
562+
"creationDate": "2020-07-15T21:38:31.152Z",
563+
"iamRoleArn": "arn:aws:iam::1112233445566:role/FullBackupTestRole",
564+
"resourceType": "Aurora",
565+
"resourceArn": "arn:aws:rds:us-west-2:1112233445566:cluster:id",
566+
"status": "COMPLETED",
567+
"isEncrypted": "false",
568+
"storageClass": "WARM",
569+
"completionDate": "2020-07-15T21:39:05.689Z",
570+
"createdBy": {
571+
"backupPlanId": "bde0f455-4e24-4668-aeaa-4932a97f5cc5",
572+
"backupPlanArn": "arn:aws:backup:us-west-2:1112233445566:backup-plan:bde0f455-4e24-4668-aeaa-4932a97f5cc5",
573+
"backupPlanVersion": "YTkzNmM0MmUtMWRhNS00Y2RkLThmZGUtNjA5NTc4NGM1YTc5",
574+
"backupPlanRuleId": "1f97bafa-14d6-4f39-94fd-94b51bd6d0d5"
575+
},
576+
"lifecycle": {
577+
"deleteAfterDays": 100
578+
},
579+
"calculatedLifeCycle": {
580+
"deleteAt": "2020-10-23T21:38:31.152Z"
581+
}
582+
}
509583
}
510584
]

internal/app/app.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ func (a *App) ParseEvent(e awsEvent.CloudWatchEvent) (events.StateChangeEvent, e
3333
return events.NewBackupVaultStateChange(e.Detail)
3434
case "Copy Job State Change":
3535
return events.NewCopyJobStateChange(e.Detail)
36+
case "Recovery Point State Change":
37+
return events.NewRecoveryPointStateChange(e.Detail)
3638
case "Region Setting State Change":
3739
return events.NewRegionSettingStateChange(e.Detail)
3840
case "Restore Job State Change":

internal/events/recovery-point.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package events
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"slices"
7+
8+
"github.com/aws/aws-sdk-go-v2/aws/arn"
9+
"github.com/slack-go/slack"
10+
)
11+
12+
type RecoveryPointStateChange struct {
13+
StateChangeEvent
14+
BackupVaultArn string `json:"backupVaultArn"`
15+
BackupVaultName string `json:"backupVaultName"`
16+
ResourceArn string `json:"resourceArn"`
17+
ResourceType string `json:"resourceType"`
18+
State string `json:"state"`
19+
Status string `json:"status"`
20+
StatusMessage string `json:"statusMessage"`
21+
DeletedBy string `json:"deletedBy"`
22+
Raw string `json:"-"`
23+
}
24+
25+
func (sce *RecoveryPointStateChange) SlackMessage() (slack.MsgOption, slack.MsgOption) {
26+
var blocks []slack.Block
27+
28+
header := slack.NewHeaderBlock(slack.NewTextBlockObject("plain_text", "Recovery Point Changed", false, false))
29+
blocks = append(blocks, header)
30+
31+
var detailFields []*slack.TextBlockObject
32+
detailFields = append(detailFields, slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*State*\n%s", sce.State), false, false))
33+
34+
if sce.BackupVaultName != "" {
35+
detailFields = append(detailFields, slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*Vault*\n%s", sce.BackupVaultName), false, false))
36+
}
37+
if sce.ResourceArn != "" {
38+
rArn, rArnErr := arn.Parse(sce.ResourceArn)
39+
if rArnErr == nil {
40+
detailFields = append(detailFields, slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*Account*\n%s", rArn.AccountID), false, false))
41+
detailFields = append(detailFields, slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*Type*\n%s", sce.ResourceType), false, false))
42+
detailFields = append(detailFields, slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*Region*\n%s", rArn.Region), false, false))
43+
detailFields = append(detailFields, slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*Resource*\n%s", rArn.Resource), false, false))
44+
}
45+
}
46+
details := slack.NewSectionBlock(nil, detailFields, nil)
47+
blocks = append(blocks, details)
48+
49+
if sce.StatusMessage != "" {
50+
desc := slack.NewSectionBlock(
51+
slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("```\n%s\n```", sce.StatusMessage), false, false),
52+
nil, nil,
53+
)
54+
blocks = append(blocks, desc)
55+
}
56+
57+
return slack.MsgOptionText(sce.StatusMessage, false), slack.MsgOptionBlocks(blocks...)
58+
}
59+
60+
func (sce *RecoveryPointStateChange) IsAlertable() bool {
61+
failedStates := []string{"DELETED"}
62+
return slices.Contains(failedStates, sce.State) && sce.DeletedBy == "MANUAL_DELETE"
63+
}
64+
65+
func NewRecoveryPointStateChange(raw json.RawMessage) (*RecoveryPointStateChange, error) {
66+
var sce RecoveryPointStateChange
67+
if err := json.Unmarshal(raw, &sce); err != nil {
68+
return &RecoveryPointStateChange{}, err
69+
}
70+
if sce.State == "" {
71+
sce.State = sce.Status
72+
}
73+
sce.StatusMessage = "Recovery point (aka backup) changed within the AWS Backup service. Please confirm it was intended."
74+
return &sce, nil
75+
}

0 commit comments

Comments
 (0)