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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ functions:
enabled: true
```

You can you can optionally configure a virtual_service name, url, basepath and tags.
```yaml
custom:
kong:
admin_api_url: http://localhost:8001
virtual_service:
name: lambda-BFF-service
url: http://localhost:8001
base_path: /bff
tags:
- serverless-plugin-kong
- lambda-BFF-virtual-service
lambda:
config:
aws_key: ${env:AWS_ACCESS_KEY_ID, 'asdf'}
aws_secret: ${env:AWS_SECRET_ACCESS_KEY, 'asdf'}
```
## Contributing

Pull requests welcome! Please fork the repo and submit your PR.
9 changes: 8 additions & 1 deletion example/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ service: example

provider:
name: aws
runtime: nodejs8.10
runtime: nodejs18.x

functions:
hello:
Expand All @@ -30,6 +30,13 @@ functions:
custom:
kong:
admin_api_url: http://localhost:8001
virtual_service:
name: lambda-BFF-service
url: http://localhost:8001
base_path: /bff
tags:
- serverless-plugin-kong
- lambda-BFF-virtual-service
lambda:
config:
aws_key: ${env:AWS_ACCESS_KEY_ID, 'asdf'}
Expand Down
123 changes: 92 additions & 31 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,23 @@ class ServerlessPlugin {

const defaultConfig = (this.serverless.config.serverless.service.custom || {}).kong || {};
const defaultLambdaConfig = (defaultConfig.lambda || {}).config || {};
const defaultVirtualService = defaultConfig.virtual_service || {};
const detaultBasePath = defaultVirtualService.base_path || '';
const detaultTags = defaultConfig.tags;

this.kong = new Kong({ adminAPIURL: defaultConfig.admin_api_url || 'http://localhost:8001' });

// Create the dummy service for Lambda
const service = await this.kong.services.createOrUpdate('lambda-dummy-service', {
url: 'http://localhost:8001',
});
// Create a Virtual Service for Lambda Functions
const serviceName = defaultVirtualService.name || this.serverless.configurationInput.service
const serviceUrl = defaultVirtualService.url || 'http://localhost:8000'
const service = await this.createVirtualService(serviceName, serviceUrl, detaultTags)

// get the current routes in Kong
const existingRoutes = [];
const newRoutes = [];
let next = null;
do {
const result = await this.listRoutes();
const result = await this.listRoutes(serviceName);
existingRoutes.push(...result.data);
next = result.next; // eslint-disable-line
} while (next !== null);
Expand All @@ -48,63 +52,120 @@ class ServerlessPlugin {
const kongEvents = f.events.filter(e => e.kong !== undefined).map(x => x.kong);

for (const event of kongEvents) {
// construct config for the aws-lambda plugin
const eventLambdaConfig = (event.lambda || {}).config || {};
const lambdaConfig = Object.assign(
{},
{ aws_region: this.region, function_name: f.name },
defaultLambdaConfig,
eventLambdaConfig,
);

// create the route and add the aws-lambda plugin
const route = await this.createRoute(service, event);
await this.addLambdaPlugin(route, lambdaConfig);
// create route and add the aws-lambda plugin
const routeConfig = this.constructRouteConfig(event, detaultBasePath, detaultTags)
const route = await this.createRoute(service, routeConfig);

const lambdaConfig = this.constructLambdaConfig(event, defaultLambdaConfig, f)
await this.addLambdaPlugin(route, lambdaConfig, detaultTags);

// add any other specified plugins
const plugins = event.plugins || [];
for (const plugin of plugins) {
await this.addPluginToRoute(route, plugin);
await this.addPluginToRoute(route, plugin, detaultTags);
}
// add name to new routes (it's importante to add this names on kong routes only after delete old routes to avoid conflit )
route.name = f.name
newRoutes.push(route)
}
}

// delete the old routes
for (const r of existingRoutes) {
await this.kong.routes.delete(r.id);
}
await this.deleteOldRoutes(existingRoutes)

await this.updateAllNewRoutesNames(newRoutes)

}

createRoute(service, event) {
createVirtualService(name, url, tags) {
return this.kong.services.createOrUpdate(name, {
url: url,
tags: tags
});
}

constructLambdaConfig(event, defaultLambdaConfig, f) {
const eventLambdaConfig = (event.lambda || {}).config || {};
const lambdaConfig = Object.assign(
{},
{ aws_region: this.region, function_name: f.name },
defaultLambdaConfig,
eventLambdaConfig,
);
return lambdaConfig
}

constructRouteConfig(event, detaultBasePath, detaultTags) {
let routeConfig = this.addBasePathToRoute(event.route, detaultBasePath)
routeConfig = this.addTagsToRoute(event.route, detaultTags)
return routeConfig
}

createRoute(service, route) {
return this.kong.routes.create({
service: { id: service.id },
...event.route,
...route,
});
}

addLambdaPlugin(route, config) {
addLambdaPlugin(route, config, tags) {
return this.kong.routes.addPlugin({
routeId: route.id,
name: 'aws-lambda',
config,
tags: tags,
enabled: true,
});
}

addPluginToRoute(route, plugin) {
addPluginToRoute(route, plugin, tags) {
plugin.tags = tags;
return this.kong.routes.addPlugin({
routeId: route.id,
...plugin,
});
}

/**
* Lists the existing routes in Kong for the lambda-dummy-service
*/
updateRouteName(route, routeName) {
return this.kong.routes.update(
route.id,
{name: routeName},
);
}

listRoutes(serviceName) {
return this.kong.routes.list({ serviceNameOrID: serviceName });
}

addBasePathToRoute(route, basePath) {
const newPaths = [];
for (const path of route.paths) {
newPaths.push(basePath + path)
}
route.paths = newPaths
return route
}

addTagsToRoute(route, tags) {
route.tags = tags
return route
}

listRoutes(offset) {
return this.kong.routes.list({ serviceNameOrID: 'lambda-dummy-service', offset });
deleteOldRoutes(existingRoutes) {
const parallelExecution = []
for (const r of existingRoutes) {
parallelExecution.push(this.kong.routes.delete(r.id));
}
return Promise.all(parallelExecution)
}

updateAllNewRoutesNames(newRoutes) {
const parallelExecution = []
for (const newRoute of newRoutes) {
parallelExecution.push(this.updateRouteName(newRoute, newRoute.name))
}
return Promise.all(parallelExecution)
}

}

module.exports = ServerlessPlugin;