├── global-alerting.json ├── package.json ├── alerting.json ├── README.md └── index.js /global-alerting.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "notificationTopicStageMapping": { 4 | "dev": "your-dev-sns-topic", 5 | "testing": "your-testing-sns-topic", 6 | "staging": "your-staging-sns-topic", 7 | "live": "your-live-sns-topic" 8 | }, 9 | "alerts": { 10 | "Throttles": { 11 | "enabled": true, 12 | "alarmNamespace": "AWS/Lambda", 13 | "description": "(Global) Alarm if function has more than 50 throttled requests", 14 | "alarmStatisticType": "Sum", 15 | "alarmPeriod": "60", 16 | "alarmThreshold": "50", 17 | "comparisonOperator": "GreaterThanOrEqualToThreshold", 18 | "evaluationPeriod": "1" 19 | } 20 | } 21 | } 22 | ] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-plugin-alerting", 3 | "version": "0.5.14", 4 | "engines": { 5 | "node": ">=4.0" 6 | }, 7 | "description": "Serverless Alerting Plugin.", 8 | "author": "https://github.com/martinlindenberg/", 9 | "license": "MIT", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/martinlindenberg/serverless-plugin-alerting" 13 | }, 14 | "keywords": [ 15 | "serverless plugin alerting", 16 | "serverless framework plugin", 17 | "serverless applications", 18 | "serverless plugins", 19 | "api gateway", 20 | "lambda", 21 | "cloudwatch alerts", 22 | "aws", 23 | "aws lambda", 24 | "amazon", 25 | "amazon web services" 26 | ], 27 | "main": "index.js", 28 | "bin": {}, 29 | "scripts": { 30 | }, 31 | "devDependencies": { 32 | }, 33 | "dependencies": { 34 | "bluebird": "^3.0.6" 35 | } 36 | } -------------------------------------------------------------------------------- /alerting.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "notificationTopicStageMapping": { 4 | "development": "your-dev-sns-topic", 5 | "testing": "your-testing-sns-topic", 6 | "staging": "your-staging-sns-topic", 7 | "live": "arn:aws:sns:eu-west-1:123456789012:another-accounts-live-sns-topic" 8 | }, 9 | "metricFilters": { 10 | "usedMemory": { 11 | "filterPattern": "[line_type=\"REPORT\",request_id_label,request_id,duration_label,duration,duration_ms,billed_duration_label1,billed_duration_label2,billed_duration,billed_duration_ms,allocated_memory_label1,allocated_memory_label2,allocated_memory,allocated_memory_mb,used_memory_label1,used_memory_label2,used_memory_label3,used_memory,used_memory_mb]", 12 | "metricTransformations": [ 13 | { 14 | "metricName": "usedMemory", 15 | "metricValue": "$used_memory" 16 | } 17 | ] 18 | } 19 | }, 20 | "subscriptionFilters": { 21 | "example": { 22 | "filterName": "myexamplefilter", 23 | "filterPattern": "", 24 | "destinationArn": "arn:aws:logs:us-east-1:01234567890:destination:foo", 25 | "roleArn": null 26 | } 27 | }, 28 | "alerts": { 29 | "Duration": { 30 | "enabled": true, 31 | "alarmNamespace": "AWS/Lambda", 32 | "description": "Alarm if duration of the function is above 500ms", 33 | "alarmStatisticType": "Maximum", 34 | "alarmPeriod": "60", 35 | "alarmThreshold": "500", 36 | "comparisonOperator": "GreaterThanOrEqualToThreshold", 37 | "evaluationPeriod": "1" 38 | }, 39 | "Errors": { 40 | "enabled": true, 41 | "alarmNamespace": "AWS/Lambda", 42 | "description": "Alarm if function returns an error", 43 | "alarmStatisticType": "Sum", 44 | "alarmPeriod": "60", 45 | "alarmThreshold": "1", 46 | "comparisonOperator": "GreaterThanOrEqualToThreshold", 47 | "evaluationPeriod": "1" 48 | }, 49 | "Throttles": { 50 | "enabled": true, 51 | "alarmNamespace": "AWS/Lambda", 52 | "description": "Alarm if function has more than 5 throttled requests", 53 | "alarmStatisticType": "Sum", 54 | "alarmPeriod": "60", 55 | "alarmThreshold": "5", 56 | "comparisonOperator": "GreaterThanOrEqualToThreshold", 57 | "evaluationPeriod": "1" 58 | } 59 | } 60 | } 61 | ] 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Serverless Plugin ALERTING 2 | ========================== 3 | 4 | [![Join the chat at https://gitter.im/martinlindenberg/serverless-plugin-alerting](https://badges.gitter.im/martinlindenberg/serverless-plugin-alerting.svg)](https://gitter.im/martinlindenberg/serverless-plugin-alerting?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | [![NPM](https://nodei.co/npm/serverless-plugin-alerting.png?downloads=true)](https://nodei.co/npm/serverless-plugin-alerting/) 7 | 8 | This Plugin adds Cloudwatch Alarms with SNS notifications for your Lambda functions. 9 | 10 | *Note*: This plugin supports Serverless 0.5.* 11 | (Please use previous releases for other sls-versions) 12 | 13 | 14 | ### Installation 15 | 16 | - make sure that aws and serverless are installed 17 | - @see http://docs.aws.amazon.com/cli/latest/userguide/installing.html 18 | - @see http://www.serverless.com/ 19 | 20 | - install this plugin to your project 21 | - (adds the plugin to your node_modules folder) 22 | 23 | ``` 24 | cd projectfolder 25 | npm install serverless-plugin-alerting 26 | ``` 27 | 28 | - add the plugin to your s-project.json file 29 | 30 | ``` 31 | "plugins": [ 32 | "serverless-plugin-alerting" 33 | ] 34 | ``` 35 | 36 | - place the alerting.json file next to your s-function.json in the directory of the function for which you want to configure alerting 37 | - *AND/OR* place a global-alerting.json file next to your s-project.json file in the projects root folder 38 | - feel free to modify it as required 39 | 40 | ### Run the Plugin 41 | 42 | - the plugin uses a hook that is called after each deployment of a function 43 | - you only have to deploy your function as usual `sls function deploy` 44 | 45 | #### Singe configuration for all functions (global-alerting.json) 46 | 47 | - copy the file global-alerting.json into your projects root folder 48 | - the provided alerts will be created for every deployed function automatically 49 | - same structure as alerting.json 50 | - these alerts were appended to the alerts defined in alerting.json 51 | 52 | #### Special configuration for every function (alerting.json) 53 | 54 | - copy the file alerting.json into your functions folder 55 | - these alerts were appended to the alerts defined in global-alerting.json 56 | 57 | ### Structure 58 | 59 | - array of alerting definition objects 60 | - you can add multiple alerts as an array of alerting-objects 61 | - you can add multiple mertic filters as in array of metricfilter-objects 62 | - you can add multiple subscription filters as in array of subscritionfilter-objects 63 | - use-case: 64 | - alert1: submit normal notifications immediately to instant messenger (Example: Threshold: Errors >= 1 for 1 minute) 65 | - alert2: submit notification to statuspage of your service to notify the customers about a problem (Example: Threshold: Duration >= 500 for 5 minutes) 66 | 67 | - required changes for multiple alerts 68 | 69 | ``` 70 | [ 71 | { 72 | "notificationTopicStageMapping": { ... }, 73 | "metricFilters": { ... }, 74 | "subscriptionFilters": { ... }, 75 | "alerts": { ... } 76 | }, 77 | { 78 | "notificationTopicStageMapping": { ... }, 79 | "metricFilters": { ... }, 80 | "subscriptionFilters": { ... }, 81 | "alerts": { ... } 82 | } 83 | ] 84 | ``` 85 | 86 | #### Notification-Topics 87 | 88 | - Here you have to define a mapping between a staging environment name and a SNS Topic that receives Messages 89 | - make sure that the staging environment exists: `sls variables list` 90 | - Serverless shows you all stages to show the variables from 91 | - select one stage and press enter or press ctrl + c (the output of this function is not important now) 92 | 93 | ``` 94 | Serverless: Select a stage: 95 | 1) dev 96 | 2) live 97 | > 3) staging 98 | 4) testing 99 | ``` 100 | 101 | - create the stages, if required: `sls stage create` 102 | - The mapped SNS Topics will be created automatically if they don't exist 103 | - *NEW 0.5.10*: You can now use global SNS-Topics that receive events from multiple Accounts: Enter the full ARN if you use another accounts SNS-Topics 104 | - What to do next: 105 | - As soon as these alerts have been created, they automatically submit notifications to these SNS-Topics 106 | - If you want to react on these alarms you can subscribe Lambda-Functions to these Topics 107 | (For example Push a notification to a messaging system like slack, send a email or push data to any Rest-Api.) 108 | - @see https://github.com/martinlindenberg/serverless-plugin-sns :) 109 | 110 | #### Metric Filters 111 | 112 | - key: name of the metric filter that needs to be created 113 | - the values were used to fill up a aws-cli command 114 | - http://docs.aws.amazon.com/cli/latest/reference/logs/put-metric-filter.html 115 | 116 | #### Subscription Filters 117 | 118 | - key: name of the subscription filter that needs to be created 119 | - the values were used to fill up a aws-cli command 120 | - http://docs.aws.amazon.com/cli/latest/reference/logs/put-subscription-filter.html 121 | 122 | #### Alerts 123 | 124 | - key: name of the metric that needs to be checked 125 | - the values were used to fill up a aws-cli command 126 | - http://docs.aws.amazon.com/cli/latest/reference/cloudwatch/put-metric-alarm.html 127 | - 0.5.8: you can define your own MetricName and Dimensions 128 | 129 | ``` 130 | [ 131 | { 132 | ... 133 | "alerts": { 134 | "Duration": { 135 | ... 136 | "metricName": "myOwnMetricName", 137 | "dimensions": [ 138 | { 139 | Name: "Resource", Value: "myResourceName" 140 | }, 141 | { 142 | Name: "FunctionName", Value: "myFunctionName" 143 | } 144 | ] 145 | } 146 | } 147 | ... 148 | } 149 | ] 150 | ``` 151 | 152 | - 0.5.11: per default the alarm triggers sns-events on all actions: InsufficientData, OK and Alarm 153 | - you can define the used actions with the following attribute 154 | 155 | ``` 156 | [ 157 | { 158 | ... 159 | "alerts": { 160 | "Duration": { 161 | ... 162 | "assignedActions": [ 163 | 'InsufficientData', 164 | 'OK', 165 | 'Alarm' 166 | ], 167 | ... 168 | } 169 | } 170 | ... 171 | } 172 | ] 173 | ``` 174 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(S) { 4 | 5 | const AWS = require('aws-sdk'), 6 | SCli = require(S.getServerlessPath('utils/cli')), 7 | SUtils = require(S.getServerlessPath('utils')), 8 | fs = require('fs'), 9 | BbPromise = require('bluebird'); // Serverless uses Bluebird Promises and we recommend you do to because they provide more than your average Promise :) 10 | 11 | class ServerlessPluginAlerting extends S.classes.Plugin { 12 | constructor(S) { 13 | super(S); 14 | } 15 | 16 | static getName() { 17 | return 'com.serverless.' + ServerlessPluginAlerting.name; 18 | } 19 | 20 | registerHooks() { 21 | 22 | S.addHook(this._addAlertsAfterDeploy.bind(this), { 23 | action: 'functionDeploy', 24 | event: 'post' 25 | }); 26 | 27 | S.addHook(this._addAlertsAfterDeploy.bind(this), { 28 | action: 'dashDeploy', 29 | event: 'post' 30 | }); 31 | 32 | return BbPromise.resolve(); 33 | } 34 | 35 | /** 36 | * adds alerts after the deployment of a function 37 | * 38 | * @param object evt 39 | * 40 | * @return promise 41 | */ 42 | _addAlertsAfterDeploy(evt) { 43 | let _this = this; 44 | 45 | return new BbPromise(function(resolve, reject) { 46 | for(var region in evt.data.deployed) { 47 | _this._manageAlerts(evt, region); 48 | } 49 | 50 | return resolve(evt); 51 | }); 52 | } 53 | 54 | /** 55 | * Handles the Creation of an alert and the required topics 56 | * 57 | * @param object evt Event 58 | * @param string region 59 | * 60 | * @return promise 61 | */ 62 | _manageAlerts (evt, region) { 63 | let _this = this; 64 | 65 | _this.stage = evt.options.stage; 66 | _this.region = region; 67 | _this._initAws(region); 68 | 69 | if (S.cli.action != 'deploy' || (S.cli.context != 'function' && S.cli.context != 'dash')) 70 | return; 71 | 72 | // merges global and local alertsettings 73 | var alertSettings = _this._mergeAlertSettings([ 74 | _this._getFunctionsAlertSettings(evt, region), 75 | _this._getProjectAlertSettings(evt, region), 76 | ]); 77 | 78 | // no settings found 79 | if (alertSettings.length == 0) { 80 | return; 81 | } 82 | 83 | var requiredTopics = _this._getRequiredTopics(alertSettings); 84 | 85 | return _this._createTopics(requiredTopics) 86 | .then(function(){ 87 | // topics exist now 88 | let _this = this; 89 | 90 | var metricFilterPromises = _this._createMetricFilters(alertSettings, _this) 91 | var subscriptionFilterPromises = _this._createSubscriptionFilters(alertSettings, _this); 92 | var alertPromises = _this._createAlerts(alertSettings, _this); 93 | 94 | if (metricFilterPromises.length > 0) { 95 | BbPromise.all(metricFilterPromises) 96 | .then(function(){ 97 | console.log('metric filters created'); 98 | }); 99 | } 100 | 101 | if (subscriptionFilterPromises.length > 0) { 102 | BbPromise.all(subscriptionFilterPromises) 103 | .then(function(){ 104 | console.log('subscription filters created'); 105 | }); 106 | } 107 | 108 | if(alertPromises.length > 0) { 109 | BbPromise.all(alertPromises) 110 | .then(function(){ 111 | console.log('alerts created'); 112 | }); 113 | } 114 | 115 | }.bind(_this)) 116 | .catch(function(e){ 117 | console.log('e', e) 118 | SCli.log('error in creating alerts', e) 119 | }); 120 | } 121 | 122 | /** 123 | * creates alerts for the function 124 | * 125 | * @param array functionAlertSettings List of settings for each deployed function 126 | * @param object _this as this function returns an array, i can not use _createAlerts(a,b).bind(_this) to attach a pointer to _this 127 | * 128 | * @return array 129 | */ 130 | _createAlerts (functionAlertSettings, _this) { 131 | 132 | var alertActions = []; 133 | var alertNamesProcessed = []; 134 | 135 | for (var i in functionAlertSettings) { 136 | var alertContents = functionAlertSettings[i]; 137 | 138 | for (var j in alertContents) { 139 | var alertContent = alertContents[j]; 140 | 141 | // only if there is a sns topic 142 | if (!alertContent.notificationTopicStageMapping[_this.stage]) { 143 | continue; 144 | } 145 | 146 | var notificationAction = _this._getNotificationActionByArn( 147 | alertContent.Arn, 148 | alertContent.notificationTopicStageMapping, 149 | _this.stage 150 | ); 151 | 152 | var functionName = _this._getFunctionNameByArn(alertContent.Arn, _this.stage); 153 | 154 | for (var metricname in alertContent.alerts) { 155 | var topicName = alertContent.notificationTopicStageMapping[_this.stage]; 156 | if (topicName.indexOf('arn:aws:sns:') >= 0) { 157 | var parts = topicName.split(':'); 158 | topicName = parts[parts.length - 1]; 159 | } 160 | 161 | var alertConfig = _this._getAlarmConfig(functionName, metricname, alertContent.alerts[metricname], _this.stage, topicName, notificationAction); 162 | 163 | if (alertNamesProcessed.indexOf(alertConfig.AlarmName) === -1) { 164 | alertNamesProcessed.push(alertConfig.AlarmName); 165 | alertActions.push( 166 | _this.aws.request('CloudWatch', 'putMetricAlarm', alertConfig, _this.stage, _this.region) 167 | ); 168 | 169 | } else 170 | console.log('skipping \''+alertConfig.AlarmName+'\', alerting.json has overriding settings.') 171 | } 172 | } 173 | } 174 | 175 | return alertActions; 176 | } 177 | 178 | /** 179 | * creates metric filters for the function 180 | * 181 | * @param array functionAlertSettings List of settings for each deployed function 182 | * @param object _this as this function returns an array, i can not use _createMetricFilters(a,b).bind(_this) to attach a pointer to _this 183 | * 184 | * @return array 185 | */ 186 | _createMetricFilters (functionAlertSettings, _this) { 187 | 188 | var metricFilterActions = []; 189 | 190 | for (var i in functionAlertSettings) { 191 | var alertContents = functionAlertSettings[i]; 192 | for (var j in alertContents) { 193 | var alertContent = alertContents[j]; 194 | if (!alertContent.metricFilters) { 195 | console.log('no metric filters defined'); 196 | return []; 197 | } 198 | var functionName = _this._getFunctionNameByArn(alertContent.Arn, _this.stage); 199 | var logGroupName = '/aws/lambda/' + functionName; 200 | 201 | for (var metricfilter in alertContent.metricFilters) { 202 | alertContent.metricFilters[metricfilter].filterName = logGroupName + '_' + metricfilter; 203 | alertContent.metricFilters[metricfilter].logGroupName = logGroupName; 204 | alertContent.metricFilters[metricfilter].metricTransformations.forEach(function (transformation, index) { 205 | if(!transformation.metricNamespace) { 206 | transformation.metricNamespace = functionName; 207 | } 208 | }); 209 | metricFilterActions.push( 210 | _this.aws.request('CloudWatchLogs', 'putMetricFilter', alertContent.metricFilters[metricfilter], _this.stage, _this.region) 211 | ); 212 | } 213 | } 214 | } 215 | return metricFilterActions; 216 | } 217 | 218 | /** 219 | * creates subscription filters for the function 220 | * 221 | * @param array functionAlertSettings List of settings for each deployed function 222 | * @param object _this as this function returns an array, i can not use _createsubscriptionFilters(a,b).bind(_this) to attach a pointer to _this 223 | * 224 | * @return array 225 | */ 226 | _createSubscriptionFilters (functionAlertSettings, _this) { 227 | var subscriptionFilterActions = []; 228 | 229 | for (var i in functionAlertSettings) { 230 | var alertContents = functionAlertSettings[i]; 231 | for (var j in alertContents) { 232 | var alertContent = alertContents[j]; 233 | if (!alertContent.subscriptionFilters) { 234 | console.log('no subscription filters defined'); 235 | return []; 236 | } 237 | var functionName = _this._getFunctionNameByArn(alertContent.Arn, _this.stage); 238 | var logGroupName = '/aws/lambda/' + functionName; 239 | 240 | for (var subscriptionFilter in alertContent.subscriptionFilters) { 241 | alertContent.subscriptionFilters[subscriptionFilter].filterName = subscriptionFilter; 242 | alertContent.subscriptionFilters[subscriptionFilter].logGroupName = logGroupName; 243 | subscriptionFilterActions.push( 244 | _this.aws.request('CloudWatchLogs', 'putSubscriptionFilter', alertContent.subscriptionFilters[subscriptionFilter], _this.stage, _this.region) 245 | ); 246 | } 247 | } 248 | } 249 | return subscriptionFilterActions; 250 | } 251 | 252 | /** 253 | * creates topics if not yet done 254 | * 255 | * @param array topics 256 | * 257 | * @return BpPromise 258 | */ 259 | _createTopics (topics) { 260 | var _this = this; 261 | _this.topics = topics; 262 | 263 | return _this.aws.request('SNS', 'listTopics', {}, _this.stage, _this.region) 264 | .then(function(topicListResult){ 265 | var _this = this; 266 | //create fast checkable topiclist['topic1'] = 'topic1' 267 | var topicList = []; 268 | if (topicListResult['Topics']) { 269 | for (var i in topicListResult.Topics) { 270 | var arnParts = topicListResult.Topics[i].TopicArn.split(':') 271 | var topicName = arnParts[arnParts.length - 1]; 272 | topicList[topicName] = topicName; 273 | } 274 | } 275 | 276 | for (var i in this.topics) { 277 | if (!topicList[i]) { 278 | console.log('topic ' + i + ' does not exist. it will be created now'); 279 | _this.aws.request('SNS', 'createTopic', {'Name': i}, _this.stage, _this.region) 280 | .then(function(){ 281 | console.log('topic created'); 282 | }) 283 | .catch(function(e){ 284 | console.log('error during creation of the topic !', e) 285 | }); 286 | } else { 287 | console.log('topic ' + i + ' exists.'); 288 | } 289 | } 290 | }.bind(this)); 291 | } 292 | 293 | /** 294 | * initializes aws 295 | * 296 | * @param string region 297 | * 298 | * @return void 299 | */ 300 | _initAws (region) { 301 | let _this = this; 302 | _this.aws = S.getProvider('aws'); 303 | } 304 | 305 | /** 306 | * finds the topics for the function 307 | * 308 | * @param array functionAlertSettings 309 | * 310 | * @return array 311 | */ 312 | _getRequiredTopics(functionAlertSettings) { 313 | let _this = this; 314 | var topics = []; 315 | 316 | for (var i in functionAlertSettings) { 317 | var alertContents = functionAlertSettings[i]; 318 | 319 | for (var j in alertContents) { 320 | var alertContent = alertContents[j]; 321 | 322 | // only if there is a sns topic 323 | if (!alertContent.notificationTopicStageMapping[_this.stage]) { 324 | continue; 325 | } 326 | 327 | var topicName = alertContent.notificationTopicStageMapping[_this.stage]; 328 | 329 | // ignore existing topics 330 | if (topicName.indexOf('arn:aws:sns:') >= 0) { 331 | continue; 332 | } 333 | 334 | topics[topicName] = topicName; 335 | } 336 | } 337 | 338 | return topics; 339 | } 340 | 341 | /** 342 | * receives a list of settings and merges them (AND-Connected) 343 | * 344 | * @param array settingsList 345 | * 346 | * @return array 347 | */ 348 | _mergeAlertSettings(settingsList){ 349 | var result = []; 350 | 351 | for (var i in settingsList) { 352 | for (var j in settingsList[i]) { 353 | result.push(settingsList[i][j]); 354 | } 355 | } 356 | 357 | return result; 358 | } 359 | 360 | /** 361 | * parses the alert json file and returns the data 362 | * 363 | * @param object evt 364 | * @param string region 365 | * 366 | * @return array 367 | */ 368 | _getFunctionsAlertSettings(evt, region){ 369 | let _this = this; 370 | var settings = []; 371 | 372 | for (var deployedIndex in evt.data.deployed[region]) { 373 | var deployed = evt.data.deployed[region][deployedIndex], 374 | functionName = deployed['functionName'], 375 | alertPathFile = S.getProject().getFunction(functionName).getFilePath().replace('s-function.json', 'alerting.json'); 376 | 377 | if (!fs.existsSync(alertPathFile)) { 378 | continue; 379 | } 380 | 381 | try { 382 | var alertContents = JSON.parse(fs.readFileSync(alertPathFile)); 383 | 384 | if (!alertContents.length > 0) { 385 | alertContents = [alertContents]; 386 | } 387 | 388 | for (var i in alertContents) { 389 | alertContents[i].Arn = deployed.Arn; 390 | } 391 | 392 | settings.push(alertContents); 393 | } catch (e) { 394 | console.log('alerting.json not readable'); 395 | continue; 396 | } 397 | } 398 | 399 | return SUtils.populate(S.getProject(), {}, settings, evt.options.stage, region); 400 | } 401 | 402 | /** 403 | * parses the global alert josn file and returns data 404 | * 405 | * @param object evt 406 | * @param string region 407 | * 408 | * @return array 409 | */ 410 | _getProjectAlertSettings(evt, region){ 411 | let _this = this; 412 | var settings = []; 413 | 414 | var globalAlertFile = S.getProject().getRootPath('global-alerting.json'); 415 | 416 | if (!fs.existsSync(globalAlertFile)) { 417 | return settings; 418 | } 419 | try { 420 | // each deployed function receives its alert settings 421 | for (var deployedIndex in evt.data.deployed[region]) { 422 | var deployed = evt.data.deployed[region][deployedIndex]; 423 | var alertContents = JSON.parse(fs.readFileSync(globalAlertFile)); 424 | 425 | if (!alertContents.length > 0) { 426 | alertContents = [alertContents]; 427 | } 428 | 429 | for (var i in alertContents) { 430 | alertContents[i].Arn = deployed.Arn; 431 | } 432 | 433 | settings.push(alertContents); 434 | } 435 | } catch (e) { 436 | console.log('global-alerting.json not readable'); 437 | } 438 | 439 | return SUtils.populate(S.getProject(), {}, settings, evt.options.stage, region); 440 | } 441 | 442 | 443 | /** 444 | * @deprecated 445 | */ 446 | _getFunctionNameByArn(arn, stage) { 447 | return arn.split(':function:')[1].replace(':' + stage, ''); 448 | } 449 | 450 | /** 451 | * set the NotificationAction by ARN 452 | * 453 | * @param string arn ARN of the function 454 | * @param array map Notificationtopic mapping 455 | * @param string stage 456 | * 457 | * 458 | * @param void 459 | */ 460 | _getNotificationActionByArn(arn, map, stage) { 461 | var actionName = map[stage]; 462 | if (actionName.indexOf('arn:aws:sns:') >= 0) { 463 | return actionName; 464 | } 465 | 466 | var name = arn.split(':function:')[0].replace(':lambda:', ':sns:'); 467 | return name + ':' + actionName; 468 | } 469 | 470 | /** 471 | * returns config object for the sns command 472 | * 473 | * @param string functionname 474 | * @param string metric 475 | * @param object alertConfig 476 | * @param string stage 477 | * @param string topicName 478 | * @param string notificationAction 479 | * 480 | * @return object 481 | */ 482 | _getAlarmConfig(functionName, metric, alertConfig, stage, topicName, notificationAction) { 483 | let resourceName = functionName + ":" + stage; 484 | let metricName = metric; 485 | if('metricName' in alertConfig) { 486 | metricName = alertConfig.metricName; 487 | } 488 | let dimensions = [{ Name: "Resource", Value: resourceName }, 489 | { Name: "FunctionName", Value: functionName } 490 | ]; 491 | if('dimensions' in alertConfig) { 492 | dimensions = alertConfig.dimensions; 493 | } 494 | var config = { 495 | AlarmName: resourceName + ' ' + metric + ' -> ' + topicName, 496 | ActionsEnabled: alertConfig.enabled == true ? true : false, 497 | ComparisonOperator: alertConfig.comparisonOperator, 498 | EvaluationPeriods: alertConfig.evaluationPeriod, 499 | MetricName: metricName, 500 | Namespace: alertConfig.alarmNamespace, 501 | Period: alertConfig.alarmPeriod, 502 | Statistic: alertConfig.alarmStatisticType, 503 | Threshold: alertConfig.alarmThreshold, 504 | AlarmDescription: alertConfig.description, 505 | Dimensions: dimensions 506 | }; 507 | 508 | if (!('assignedActions' in alertConfig)) { 509 | alertConfig['assignedActions'] = [ 510 | 'InsufficientData', 511 | 'OK', 512 | 'Alarm' 513 | ]; 514 | } 515 | 516 | for (var i in alertConfig['assignedActions']) { 517 | var key = alertConfig['assignedActions'][i] + 'Actions'; 518 | config[key] = [notificationAction]; 519 | } 520 | 521 | return config; 522 | } 523 | } 524 | 525 | return ServerlessPluginAlerting; 526 | }; 527 | --------------------------------------------------------------------------------