Skip to content

Commit

Permalink
Better error message when function already exists.
Browse files Browse the repository at this point in the history
Add to docs.
  • Loading branch information
rgfindl committed Nov 9, 2017
1 parent e3e8a40 commit a6554d1
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 9 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
node_modules
.idea
pics
.idea
186 changes: 183 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,193 @@
# FunctionCI
FunctionCI is an open source CI/CD Slack Bot for AWS Lambda's.
<p align="center">
<img width="100%" src="docs/banner.png">
</p>

## Installation
# Function CI
FunctionCI is an open source Continuous Integration tool for AWS Lambda's.

Builds are done by AWS CodeBuild and AWS CodePipeline.

Receive build notifications via Slack. Deploy via Slack. View build and deployment audit trails via Slack.

Fork this repo and install FunctionCI in your AWS account, then start managing your Lambda's properly.

## Features
* Serverless - Function CI only costs money when you use it
* Github integration
* Slack bot
* Versioned build artifacts stored in S3
* Build and deployment audit trails with Github commit version and Slack username

## Table of content
- [Usage](#usage)
- [Setup](#setup)
- [Commands](#commands)
- [Architecture](#architecture)

## Usage
Use the `/fn create project` Slack command to create a new build project.

<p align="left">
<img width="60%" src="docs/create-dialog.png">
</p>

Once your build version is ready...

<p align="left">
<img width="60%" src="docs/build-messages.png">
</p>

Deploy it using the `/fn deploy fn <fn-name> <project-name> <version>` Slack command.

## Setup
1. Create the `kms` stack.
2. Configure your Slack app.
3. Encrypt your Slack params.
4. Create the `app` stack.
5. Add the URLS to your Slack app.

## Commands
- [/fn create project](#fn-create-project)
- [/fn show projeect](#fn-show-project)
- [/fn show projeects](#fn-show-projects)
- [/fn delete projeect](#fn-delete-project)
- [/fn add fn](#fn-add-fn)
- [/fn show fn](#fn-show-fn)
- [/fn show fns](#fn-show-fns)
- [/fn deploy fn](#fn-deploy-fn)
- [/fn delete fn](#fn-delete-fn)

### /fn create project
`/fn create project`

Register a new Github repo that you wish FunctionCI to build.

FunctionCI uses [CodeBuild](http://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html), so you will need a `buildspec.yml` in the root of your repo.

Here is an example `buildspec.yml` for a node.js project.

** Note you must export a single zip artifact.

```
version: 0.2
# aws/codebuild/eb-nodejs-6.10.0-amazonlinux-64:4.0.0
phases:
install:
commands:
pre_build:
commands:
- echo Installing source NPM dependencies...
- npm install
build:
commands:
- echo Testing the code
- npm test
post_build:
commands:
- echo Removing dev dependencies
- rm -Rf node_modules
- npm install --production
artifacts:
files:
- '**/*'
type: zip
```

### /fn show project
`/fn show project <project_id>`

Show the most recent builds in reverse chronological order.

The `project_id` will be `<your repo name>-<branch>`.

Example:
* Repo: https://github.com/rgfindl/functionci-demo
* Branch: master

The `project_id` == `functionci-demo-master`

<p align="left">
<img width="75%" src="docs/show-project.png">
</p>

### /fn show projects
`/fn show projects`

Show all the projects under management.

### /fn delete project
`/fn show project <project_id>`

Delete the project and all build artifacts.

### /fn add fn
`/fn add fn <short_name> <lambda-name>`

Add a Lambda function you wish to deploy build artifacts to.

Ex: `/fn add fn demo functionci-demo-LambdaFunction-LFREQ3DEC3UJ`

### /fn show fn
`/fn show fn <short_name>`

Show a function and all its deployment history.

Ex: `/fn show fn demo`

<p align="left">
<img width="75%" src="docs/show-fn.png">
</p>

### /fn show fns
`/fn show fns`

Show all the functions under management

Ex: `/fn show fns`

### /fn deploy fn
`/fn deploy fn <short_name> <project_id> <version>`

Deploy a build artifact to the Lambda function.

Ex: `/fn deploy fn demo functionci-demo-master 1`

### /fn delete fn
`/fn delete fn <short_name>`

Delete a function and all its deployment history.

Ex: `/fn delete fn demo`

## Architecture
FunctionCI is also a serverless Lambda app. It includes the following AWS resources:
* API Gateway (Exposes an API for our Slack bot)
* Lambda (Slack bot API, SNS events, CloudWatch events, CodePipeline events)
* DynamoDB (Document storage)
* S3 (Build artifacts)
* CloudFormation (Builds a pipeline for each project)
* CodePipeline and CodeBuild (Performs builds)

### Create Project
When you create a new project FunctionCI creates a new CloudFormation stack. This stack is the build pipeline for your project.
<p align="left">
<img width="100%" src="docs/FunctionCI-Create-Project.png">
</p>

### Build Project
When you commit changes to your Github repo CodePipeline is triggered. The first CodePipeline stage is CodeBuild to build, test, and package your app. The next CodePipeline stage is the FunctionCI Lambda to version and archive the build artifact.
<p align="left">
<img width="100%" src="docs/FunctionCI-Build-Project.png">
</p>

### Deploy Lambda
Now we are ready to deploy our build artifact to our Lambda.
<p align="left">
<img width="75%" src="docs/FunctionCI-Deploy-Fn.png">
</p>

## TODO
- Add howto doc with setup guide and command guide
- Lambda Versions & Aliases???
Expand Down
21 changes: 18 additions & 3 deletions app/lib/dao.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ functions.put_project = function(input, done) {
ConditionExpression: "attribute_not_exists(build_count)"
};
console.log(JSON.stringify(params));
docClient.put(params, done);
docClient.put(params, function(err, data) {
if (err && _.isEqual(err.message, 'The conditional request failed'))
return done('Project already exists.', null);
else if (err) return done(err, null);
else return done(null, data);
});
};

functions.update_project_build_count = function(input, done) {
Expand Down Expand Up @@ -103,7 +108,12 @@ functions.put_fn = function(input, done) {
ConditionExpression: "attribute_not_exists(arn)"
};
console.log(JSON.stringify(params));
docClient.put(params, next);
docClient.put(params, function(err, data) {
if (err && _.isEqual(err.message, 'The conditional request failed'))
return done('Function already exists.', null);
else if (err) return done(err, null);
else return done(null, data);
});
},
function(results, next) {
input.hash_key = 'function';
Expand All @@ -114,7 +124,12 @@ functions.put_fn = function(input, done) {
ConditionExpression: "attribute_not_exists(arn)"
};
console.log(JSON.stringify(params));
docClient.put(params, next);
docClient.put(params, function(err, data) {
if (err && _.isEqual(err.message, 'The conditional request failed'))
return done('Function already exists.', null);
else if (err) return done(err, null);
else return done(null, data);
});
}
], done);
};
Expand Down
11 changes: 10 additions & 1 deletion app/lib/handle_slack_commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,16 @@ functions.add_fn = function(params, callback) {
short_name: short_name,
arn: arn
}, function(err, data) {
if (err) {
if (err && _.isString(err) && _.includes(err, 'already exists')) {

return callback(null, {
statusCode: 200,
body: JSON.stringify({
response_type: "in_channel",
text: err
})
});
} else if (err) {
console.log(err);
return callback(null, {
statusCode: 200,
Expand Down
Binary file added docs/FunctionCI-Build-Project.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/FunctionCI-Create-Project.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/FunctionCI-Deploy-Fn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/banner.pxm
Binary file not shown.
Binary file added docs/build-messages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/create-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/show-fn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/show-project.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/slack-lambda.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a6554d1

Please sign in to comment.