Skip to content
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ custom:

## Change Log

* v1.1.0: Fix SSM environment variables resolving issues with serverless v3, change default for `BUCKET_MARKER_LOCAL` to `hot-reload`
* v1.0.6: Add `BUCKET_MARKER_LOCAL` configuration for customizing S3 bucket for lambda mount and [Hot Reloading](https://docs.localstack.cloud/user-guide/tools/lambda-tools/hot-reloading/).
* v1.0.5: Fix S3 Bucket LocationConstraint issue when the provider region is `us-east-1`
* v1.0.4: Fix IPv4 fallback check to prevent IPv6 connection issue with `localhost` on macOS
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-localstack",
"version": "1.0.6",
"version": "1.1.0",
"description": "Connect Serverless to LocalStack!",
"main": "src/index.js",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion spec/helpers/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const defaultConfig = {
provider: {
name: 'aws',
runtime: 'nodejs12.x',
lambdaHashingVersion: '20201221'
lambdaHashingVersion: '20201221',
environment: {
LAMBDA_STAGE: '${ssm:/${opt:stage, self:provider.stage}/lambda/common/LAMBDA_STAGE}'
}
},
plugins: [
'serverless-localstack'
Expand Down
28 changes: 25 additions & 3 deletions spec/integration/integration.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,33 @@
const services = require('../helpers/services');

const LONG_TIMEOUT = 30000;
const AWS = require('aws-sdk');

// Set the region and endpoint in the config for LocalStack
AWS.config.update({
region: 'us-east-1',
endpoint: 'http://localhost:4566'
});
AWS.config.credentials = new AWS.Credentials({
accessKeyId: 'test',
secretAccessKey: 'test',
});

const ssm = new AWS.SSM();

const params = {
Name: '/dev/lambda/common/LAMBDA_STAGE',
Type: 'String',
Value: 'my-value',
Overwrite: true
};

describe('LocalstackPlugin', () => {

beforeEach( () => {
this.service = services.createService({});
beforeEach(
async () => {
await ssm.putParameter(params).promise();
this.service = services.createService({});
});

afterEach( () => {
Expand All @@ -18,4 +40,4 @@ describe('LocalstackPlugin', () => {
services.deployService(this.service);
}, LONG_TIMEOUT);

});
});
4 changes: 2 additions & 2 deletions spec/unit/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe("LocalstackPlugin", () => {
instance = new LocalstackPlugin(serverless, defaultPluginState);
await simulateBeforeDeployHooks(instance);

awsProvider.request('s3', 'foo', {
await awsProvider.request('s3', 'foo', {
TemplateURL: pathToTemplate
});
expect(request.called).to.be.true;
Expand All @@ -184,7 +184,7 @@ describe("LocalstackPlugin", () => {
instance = new LocalstackPlugin(serverless, defaultPluginState)
await simulateBeforeDeployHooks(instance);

awsProvider.request('S3', 'validateTemplate', {});
await awsProvider.request('S3', 'validateTemplate', {});

expect(request.called).to.be.false;
});
Expand Down
30 changes: 14 additions & 16 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ class LocalstackPlugin {

this.serverless = serverless;
this.options = options;

this.hooks = {};
this.hooks = {'initialize': () => this.init()};
// Define a before-hook for all event types
for (let event in this.serverless.pluginManager.hooks) {
const doAdd = event.startsWith('before:');
Expand Down Expand Up @@ -152,6 +151,10 @@ class LocalstackPlugin {
}
}

async init() {
await this.reconfigureAWS()
}

addHookInFirstPosition(eventName, hookFunction) {
this.serverless.pluginManager.hooks[eventName] = this.serverless.pluginManager.hooks[eventName] || [];
this.serverless.pluginManager.hooks[eventName].unshift(
Expand All @@ -172,16 +175,6 @@ class LocalstackPlugin {
awsProvider.request = this.interceptRequest.bind(this);
}

// Reconfigure AWS clients
try {
this.reconfigureAWS();
} catch (e) {
// This can happen if we are executing in the plugin initialization context and
// the template variables have not been fully initialized yet
// (e.g., "Error: Profile ${self:custom.stage}Profile does not exist")
return;
}

// Patch plugin methods
this.skipIfMountLambda('Package', 'packageService');
function compileFunction(functionName) {
Expand All @@ -196,8 +189,7 @@ class LocalstackPlugin {
Object.keys(resources).forEach(id => {
const res = resources[id];
if (res.Type === 'AWS::Lambda::Function') {
// TODO: change the default BUCKET_MARKER_LOCAL to 'hot-reload'
res.Properties.Code.S3Bucket = process.env.BUCKET_MARKER_LOCAL || '__local__'; // for now, the default is still __local__
res.Properties.Code.S3Bucket = process.env.BUCKET_MARKER_LOCAL || 'hot-reload'; // default changed to 'hot-reload' with LS v2 release
res.Properties.Code.S3Key = process.cwd();
const mountCode = this.config.lambda.mountCode;
if (typeof mountCode === 'string' && mountCode.toLowerCase() !== 'true') {
Expand Down Expand Up @@ -574,6 +566,10 @@ class LocalstackPlugin {
*/
async reconfigureAWS() {
if(this.isActive()) {
if(this.reconfiguredEndpoints){
this.debug("Skipping reconfiguring of endpoints (already reconfigured)")
return;
}
this.log('Using serverless-localstack');
const hostname = await this.getConnectHostname();
const host = `http://${hostname}`;
Expand Down Expand Up @@ -627,6 +623,8 @@ class LocalstackPlugin {
// required for compatibility with certain plugin, e.g., serverless-domain-manager
awsProvider.cachedCredentials.endpoint = localEndpoint;
}
this.log("serverless-localstack: Reconfigured endpoints")
this.reconfiguredEndpoints = true;
}
else {
this.endpoints = {}
Expand Down Expand Up @@ -656,14 +654,13 @@ class LocalstackPlugin {
}
}

interceptRequest(service, method, params) {
async interceptRequest(service, method, params) {

// Enable the plugin here, if not yet enabled (the function call below is idempotent).
// TODO: It seems that we can potentially remove the hooks / plugin loading logic
// entirely and only rely on activating the -> we should evaluate this, as it would
// substantially simplify the code in this file.
this.beforeEventHook();

// Template validation is not supported in LocalStack
if (method == "validateTemplate") {
this.log('Skipping template validation: Unsupported in Localstack');
Expand All @@ -679,6 +676,7 @@ class LocalstackPlugin {
params.TemplateURL = params.TemplateURL.replace(/https:\/\/s3.amazonaws.com/, config.s3.endpoint);
}
}
await this.reconfigureAWS();

return this.awsProviderRequest(service, method, params);
}
Expand Down