New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

serverless-plugin-canary-deployments

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

serverless-plugin-canary-deployments - npm Package Compare versions

Comparing version 0.4.6 to 0.4.7

fixtures/7.input.cloudwatch-logs-trigger.json

3

lib/CfTemplateGenerators/index.js

@@ -8,3 +8,5 @@ const CodeDeploy = require('./CodeDeploy');

const CloudWatchEvents = require('./CloudWatchEvents');
const CloudWatchLogs = require('./CloudWatchLogs');
module.exports.codeDeploy = CodeDeploy;

@@ -17,1 +19,2 @@ module.exports.iam = Iam;

module.exports.cloudWatchEvents = CloudWatchEvents;
module.exports.cloudWatchLogs = CloudWatchLogs;

@@ -1,2 +0,6 @@

function replaceTopicSubscriptionFunctionWithAlias(snsTopic, functionAlias, functionName) {
function replaceTopicSubscriptionFunctionWithAlias(
snsTopic,
functionAlias,
functionName
) {
const subscriptions = snsTopic.Properties.Subscription;

@@ -6,3 +10,3 @@

const endpoint = subscription.Endpoint || {};
const funcDetails = (endpoint['Fn::GetAtt'] || []);
const funcDetails = endpoint['Fn::GetAtt'] || [];
const [funcName] = funcDetails;

@@ -13,13 +17,45 @@ return funcName ? funcName === functionName : false;

const restOfSubscriptions = subscriptions.filter(s => !isTargetSubscription(s));
const subscriptionWithAlias = { Endpoint: { Ref: functionAlias }, Protocol: 'lambda' };
const subscriptionWithAlias = {
Endpoint: { Ref: functionAlias },
Protocol: 'lambda'
};
const newSubscriptions = [...restOfSubscriptions, subscriptionWithAlias];
const newProperties = Object.assign({}, snsTopic.Properties, { Subscription: newSubscriptions });
const newProperties = Object.assign({}, snsTopic.Properties, {
Subscription: newSubscriptions
});
return Object.assign({}, snsTopic, { Properties: newProperties });
}
function replaceSubscriptionFunctionWithAlias(
subscription,
functionAlias,
functionName
) {
if (subscription.Properties.Protocol !== 'lambda') return subscription;
const isTargetSubscription = () => {
const endpoint = subscription.Properties.Endpoint || {};
const funcDetails = endpoint['Fn::GetAtt'] || [];
const [funcName] = funcDetails;
return funcName ? funcName === functionName : false;
};
if (!isTargetSubscription()) {
return subscription;
}
const newEndpoint = {
Ref: functionAlias
};
const newProperties = Object.assign({}, subscription.Properties, {
Endpoint: newEndpoint
});
return Object.assign({}, subscription, { Properties: newProperties });
}
const Sns = {
replaceTopicSubscriptionFunctionWithAlias
replaceTopicSubscriptionFunctionWithAlias,
replaceSubscriptionFunctionWithAlias
};
module.exports = Sns;

@@ -54,2 +54,49 @@ const { expect } = require('chai');

});
describe('.replaceSubscriptionFunctionWithAlias', () => {
const functionName = 'HelloLambdaFunction';
it("replace the sns subscription's function for an alias", () => {
const snsSubscription = {
Type: 'AWS::SNS::Subscription',
Properties: {
TopicArn: {
Ref: 'snsTopic'
},
Protocol: 'lambda',
Endpoint: {
'Fn::GetAtt': [
functionName,
'Arn'
]
},
FilterPolicy: {
eventType: [
'snsFoo'
]
}
}
};
const functionAlias = 'TheFunctionAlias';
const expected = {
Type: 'AWS::SNS::Subscription',
Properties: {
TopicArn: {
Ref: 'snsTopic'
},
Protocol: 'lambda',
Endpoint: {
Ref: functionAlias
},
FilterPolicy: {
eventType: [
'snsFoo'
]
}
}
};
const actual = Sns.replaceSubscriptionFunctionWithAlias(snsSubscription, functionAlias, functionName);
expect(actual).to.deep.equal(expected);
});
});
});

2

package.json

@@ -6,3 +6,3 @@ {

},
"version": "0.4.6",
"version": "0.4.7",
"description": "A Serverless plugin to implement canary deployment of Lambda functions",

@@ -9,0 +9,0 @@ "main": "serverless-plugin-canary-deployments.js",

@@ -66,3 +66,3 @@ [![npm version](https://badge.fury.io/js/serverless-plugin-canary-deployments.svg)](https://badge.fury.io/js/serverless-plugin-canary-deployments)

* `postTrafficHook`: (optional) validation Lambda function that runs after traffic shifting. It must use te CodeDeploy SDK to notify about this step's success or failure (more info [here](https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html))
* `alarms`: (optional) list of CloudWatch alarms. If any of them is triggered duringt the deployment, the associated Lambda function will automatically roll back to the previous version.
* `alarms`: (optional) list of CloudWatch alarms. If any of them is triggered during the deployment, the associated Lambda function will automatically roll back to the previous version.
* `triggerConfigurations`: (optional) list of CodeDeploy Triggers. See more details in the [CodeDeploy TriggerConfiguration Documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codedeploy-deploymentgroup-triggerconfig.html), or [this CodeDeploy notifications guide](https://docs.aws.amazon.com/codedeploy/latest/userguide/monitoring-sns-event-notifications-create-trigger.html) for example uses

@@ -101,6 +101,15 @@

For now, the plugin only works with Lambda functions invoked by API Gateway, Stream based (such as the triggered by Kinesis or DynamoDB Streams), SNS based events, S3 events and CloudWatch events. More events will be added soon.
For now, the plugin only works with Lambda functions invoked by
* API Gateway
* Stream based (such as the triggered by Kinesis or DynamoDB Streams)
* SNS based events
* S3 events
* CloudWatch Scheduled events
* CloudWatch Logs
[More events](https://serverless.com/framework/docs/providers/aws/events/) will be added soon.
## License
ISC © [David García](https://github.com/davidgf)

@@ -146,4 +146,6 @@ const _ = require('lodash/fp');

'AWS::SNS::Topic': CfGenerators.sns.replaceTopicSubscriptionFunctionWithAlias,
'AWS::SNS::Subscription': CfGenerators.sns.replaceSubscriptionFunctionWithAlias,
'AWS::S3::Bucket': CfGenerators.s3.replaceS3BucketFunctionWithAlias,
'AWS::Events::Rule': CfGenerators.cloudWatchEvents.replaceCloudWatchEventRuleTargetWithAlias
'AWS::Events::Rule': CfGenerators.cloudWatchEvents.replaceCloudWatchEventRuleTargetWithAlias,
'AWS::Logs::SubscriptionFilter': CfGenerators.cloudWatchLogs.replaceCloudWatchLogsDestinationArnWithAlias
};

@@ -163,5 +165,16 @@ const functionEvents = this.getEventsFor(functionName);

const snsTopics = this.getSnsTopicsFor(functionName);
const snsSubscriptions = this.getSnsSubscriptionsFor(functionName);
const s3Events = this.getS3EventsFor(functionName);
const cloudWatchEvents = this.getCloudWatchEventsFor(functionName);
return Object.assign({}, apiGatewayMethods, eventSourceMappings, snsTopics, s3Events, cloudWatchEvents);
const cloudWatchLogs = this.getCloudWatchLogsFor(functionName);
return Object.assign(
{},
apiGatewayMethods,
eventSourceMappings,
snsTopics,
s3Events,
cloudWatchEvents,
cloudWatchLogs,
snsSubscriptions
);
}

@@ -212,2 +225,12 @@

getSnsSubscriptionsFor(functionName) {
const isEventSourceMapping = _.matchesProperty('Type', 'AWS::SNS::Subscription');
const isSubscriptionForFunction = _.matchesProperty('Properties.Endpoint.Fn::GetAtt[0]', functionName);
const getMappingsForFunction = _.pipe(
_.pickBy(isEventSourceMapping),
_.pickBy(isSubscriptionForFunction)
);
return getMappingsForFunction(this.compiledTpl.Resources);
}
getCloudWatchEventsFor(functionName) {

@@ -228,2 +251,16 @@ const isEventSourceMapping = _.matchesProperty('Type', 'AWS::Events::Rule');

getCloudWatchLogsFor(functionName) {
const isEventSourceMapping = _.matchesProperty('Type', 'AWS::Logs::SubscriptionFilter');
const isMappingForFunction = _.pipe(
_.prop('Properties.DestinationArn.Fn::GetAtt'),
_.flatten,
_.includes(functionName)
);
const getMappingsForFunction = _.pipe(
_.pickBy(isEventSourceMapping),
_.pickBy(isMappingForFunction)
);
return getMappingsForFunction(this.compiledTpl.Resources);
}
getS3EventsFor(functionName) {

@@ -230,0 +267,0 @@ const isEventSourceMapping = _.matchesProperty('Type', 'AWS::S3::Bucket');

@@ -19,19 +19,20 @@ const fs = require('fs');

describe('addCanaryDeploymentResources', () => {
before(() => {
const testCaseFiles = fs.readdirSync(fixturesPath);
const getTestCaseName = _.pipe(_.split('.'), _.head);
const testCaseFileType = _.pipe(_.split('.'), _.get('[1]'));
const testCaseContentsFromFiles = _.reduce((acc, fileName) => {
const contents = JSON.parse(fs.readFileSync(path.resolve(fixturesPath, fileName)));
return _.set(testCaseFileType(fileName), contents, acc);
}, {});
const testCaseFilesByName = _.groupBy(getTestCaseName, testCaseFiles);
this.testCases = _.map(
caseName => testCaseContentsFromFiles(testCaseFilesByName[caseName]),
Object.keys(testCaseFilesByName)
);
});
const testCaseFiles = fs.readdirSync(fixturesPath);
const getTestCaseName = _.pipe(_.split('.'), _.head);
const testCaseFileType = _.pipe(_.split('.'), _.get('[1]'));
const testCaseContentsFromFiles = _.reduce((acc, fileName) => {
const contents = JSON.parse(fs.readFileSync(path.resolve(fixturesPath, fileName)));
return _.set(testCaseFileType(fileName), contents, acc);
}, {});
const testCaseFilesByName = _.groupBy(getTestCaseName, testCaseFiles);
this.testCases = _.map(
(caseName) => {
const testCaseContents = testCaseContentsFromFiles(testCaseFilesByName[caseName]);
return Object.assign(testCaseContents, {caseName});
},
Object.keys(testCaseFilesByName)
);
it('generates the correct CloudFormation templates', () => {
this.testCases.forEach(({ input, output, service }) => {
this.testCases.forEach(({ caseName, input, output, service }) => {
it(`generates the correct CloudFormation templates: test case ${caseName}`, () => {
const serverless = new Serverless(options);

@@ -38,0 +39,0 @@ Object.assign(serverless.service, service);

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc