Skip to content
Open
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
69 changes: 69 additions & 0 deletions events/cloudwatch_alarms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package events

// CloudWatchAlarmEvent is the event sent to a Lambda function when it is configured as a CloudWatch alarm action.
// See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/alarms-and-actions-Lambda.html
type CloudWatchAlarmEvent struct {
Source string `json:"source"`
AlarmARN string `json:"alarmArn"`
AccountID string `json:"accountId"`
Time string `json:"time"`
Region string `json:"region"`
AlarmData CloudWatchAlarmData `json:"alarmData"`
}

// CloudWatchAlarmData describes the alarm, its current and previous state, and its configuration.
type CloudWatchAlarmData struct {
AlarmName string `json:"alarmName"`
State CloudWatchAlarmState `json:"state"`
PreviousState CloudWatchAlarmState `json:"previousState"`
Configuration CloudWatchAlarmConfiguration `json:"configuration"`
AlarmTags []CloudWatchAlarmTag `json:"alarmTags,omitempty"`
AlarmTagsStatus string `json:"alarmTagsStatus,omitempty"`
}

// CloudWatchAlarmState is a state of the alarm. ReasonData is a JSON-encoded string.
type CloudWatchAlarmState struct {
Value string `json:"value"`
Reason string `json:"reason"`
ReasonData string `json:"reasonData,omitempty"`
Timestamp string `json:"timestamp"`
ActionsSuppressedBy string `json:"actionsSuppressedBy,omitempty"`
ActionsSuppressedReason string `json:"actionsSuppressedReason,omitempty"`
}

// CloudWatchAlarmConfiguration holds the configuration of a metric alarm (Metrics)
// or a composite alarm (AlarmRule and the ActionsSuppressor fields).
type CloudWatchAlarmConfiguration struct {
Description string `json:"description,omitempty"`
Metrics []CloudWatchAlarmMetricDataQuery `json:"metrics,omitempty"`
AlarmRule string `json:"alarmRule,omitempty"`
ActionsSuppressor string `json:"actionsSuppressor,omitempty"`
ActionsSuppressorWaitPeriod int64 `json:"actionsSuppressorWaitPeriod,omitempty"`
ActionsSuppressorExtensionPeriod int64 `json:"actionsSuppressorExtensionPeriod,omitempty"`
}

type CloudWatchAlarmMetricDataQuery struct {
ID string `json:"id"`
Expression string `json:"expression,omitempty"`
Label string `json:"label,omitempty"`
MetricStat *CloudWatchAlarmMetricStat `json:"metricStat,omitempty"`
ReturnData bool `json:"returnData"`
}

type CloudWatchAlarmMetricStat struct {
Metric CloudWatchAlarmMetric `json:"metric"`
Period int64 `json:"period"`
Stat string `json:"stat"`
Unit string `json:"unit,omitempty"`
}

type CloudWatchAlarmMetric struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Dimensions map[string]string `json:"dimensions,omitempty"`
}

type CloudWatchAlarmTag struct {
Key string `json:"key"`
Value string `json:"value"`
}
60 changes: 60 additions & 0 deletions events/cloudwatch_alarms_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package events

import (
"encoding/json"
"testing"

"github.com/aws/aws-lambda-go/events/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCloudWatchAlarmEventMarshaling(t *testing.T) {
tests := []struct {
name string
filePath string
}{
{"metric alarm", "./testdata/cloudwatch-alarm-metric-event.json"},
{"composite alarm", "./testdata/cloudwatch-alarm-composite-event.json"},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
inputJSON := test.ReadJSONFromFile(t, tc.filePath)

var inputEvent CloudWatchAlarmEvent
require.NoError(t, json.Unmarshal(inputJSON, &inputEvent))

outputJSON, err := json.Marshal(inputEvent)
require.NoError(t, err)

assert.JSONEq(t, string(inputJSON), string(outputJSON))
})
}
}

func TestCloudWatchAlarmEventFields(t *testing.T) {
var metricEvent CloudWatchAlarmEvent
require.NoError(t, json.Unmarshal(test.ReadJSONFromFile(t, "./testdata/cloudwatch-alarm-metric-event.json"), &metricEvent))

assert.Equal(t, "ALARM", metricEvent.AlarmData.State.Value)
assert.Equal(t, "INSUFFICIENT_DATA", metricEvent.AlarmData.PreviousState.Value)
require.Len(t, metricEvent.AlarmData.Configuration.Metrics, 1)
metric := metricEvent.AlarmData.Configuration.Metrics[0]
require.NotNil(t, metric.MetricStat)
assert.Equal(t, "i-12345678", metric.MetricStat.Metric.Dimensions["InstanceId"])
assert.True(t, metric.ReturnData)
assert.Equal(t, []CloudWatchAlarmTag{{Key: "Environment", Value: "prod"}}, metricEvent.AlarmData.AlarmTags)

var compositeEvent CloudWatchAlarmEvent
require.NoError(t, json.Unmarshal(test.ReadJSONFromFile(t, "./testdata/cloudwatch-alarm-composite-event.json"), &compositeEvent))

assert.Equal(t, "ALARM(CompositeDemo.FirstChild) OR ALARM(CompositeDemo.SecondChild)", compositeEvent.AlarmData.Configuration.AlarmRule)
assert.Equal(t, int64(120), compositeEvent.AlarmData.Configuration.ActionsSuppressorWaitPeriod)
assert.Equal(t, "WaitPeriod", compositeEvent.AlarmData.PreviousState.ActionsSuppressedBy)
assert.Empty(t, compositeEvent.AlarmData.Configuration.Metrics)
}

func TestCloudWatchAlarmEventMalformedJson(t *testing.T) {
test.TestMalformedJson(t, &CloudWatchAlarmEvent{})
}
30 changes: 30 additions & 0 deletions events/testdata/cloudwatch-alarm-composite-event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"source": "aws.cloudwatch",
"alarmArn": "arn:aws:cloudwatch:us-east-1:111122223333:alarm:SuppressionDemo.Main",
"accountId": "111122223333",
"time": "2023-08-04T12:56:46.138+0000",
"region": "us-east-1",
"alarmData": {
"alarmName": "CompositeDemo.Main",
"state": {
"value": "ALARM",
"reason": "arn:aws:cloudwatch:us-east-1:111122223333:alarm:CompositeDemo.FirstChild transitioned to ALARM at Friday 04 August, 2023 12:54:46 UTC",
"reasonData": "{\"triggeringAlarms\":[{\"arn\":\"arn:aws:cloudwatch:us-east-1:111122223333:alarm:CompositeDemo.FirstChild\",\"state\":{\"value\":\"ALARM\",\"timestamp\":\"2023-08-04T12:54:46.138+0000\"}}]}",
"timestamp": "2023-08-04T12:56:46.138+0000"
},
"previousState": {
"value": "ALARM",
"reason": "arn:aws:cloudwatch:us-east-1:111122223333:alarm:CompositeDemo.FirstChild transitioned to ALARM at Friday 04 August, 2023 12:54:46 UTC",
"reasonData": "{\"triggeringAlarms\":[{\"arn\":\"arn:aws:cloudwatch:us-east-1:111122223333:alarm:CompositeDemo.FirstChild\",\"state\":{\"value\":\"ALARM\",\"timestamp\":\"2023-08-04T12:54:46.138+0000\"}}]}",
"timestamp": "2023-08-04T12:54:46.138+0000",
"actionsSuppressedBy": "WaitPeriod",
"actionsSuppressedReason": "Actions suppressed by WaitPeriod"
},
"configuration": {
"alarmRule": "ALARM(CompositeDemo.FirstChild) OR ALARM(CompositeDemo.SecondChild)",
"actionsSuppressor": "CompositeDemo.ActionsSuppressor",
"actionsSuppressorWaitPeriod": 120,
"actionsSuppressorExtensionPeriod": 180
}
}
}
49 changes: 49 additions & 0 deletions events/testdata/cloudwatch-alarm-metric-event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"source": "aws.cloudwatch",
"alarmArn": "arn:aws:cloudwatch:us-east-1:444455556666:alarm:lambda-demo-metric-alarm",
"accountId": "444455556666",
"time": "2023-08-04T12:36:15.490+0000",
"region": "us-east-1",
"alarmData": {
"alarmName": "lambda-demo-metric-alarm",
"state": {
"value": "ALARM",
"reason": "test",
"timestamp": "2023-08-04T12:36:15.490+0000"
},
"previousState": {
"value": "INSUFFICIENT_DATA",
"reason": "Insufficient Data: 5 datapoints were unknown.",
"reasonData": "{\"version\":\"1.0\",\"queryDate\":\"2023-08-04T12:31:29.591+0000\",\"statistic\":\"Average\",\"period\":60,\"recentDatapoints\":[],\"threshold\":5.0,\"evaluatedDatapoints\":[{\"timestamp\":\"2023-08-04T12:30:00.000+0000\"},{\"timestamp\":\"2023-08-04T12:29:00.000+0000\"},{\"timestamp\":\"2023-08-04T12:28:00.000+0000\"},{\"timestamp\":\"2023-08-04T12:27:00.000+0000\"},{\"timestamp\":\"2023-08-04T12:26:00.000+0000\"}]}",
"timestamp": "2023-08-04T12:31:29.595+0000"
},
"configuration": {
"description": "Metric Alarm to test Lambda actions",
"metrics": [
{
"id": "1234e046-06f0-a3da-9534-EXAMPLEe4c",
"metricStat": {
"metric": {
"namespace": "AWS/Logs",
"name": "CallCount",
"dimensions": {
"InstanceId": "i-12345678"
}
},
"period": 60,
"stat": "Average",
"unit": "Percent"
},
"returnData": true
}
]
},
"alarmTags": [
{
"key": "Environment",
"value": "prod"
}
],
"alarmTagsStatus": "SUCCESS"
}
}
Loading