Skip to content

Commit

Permalink
feat(deploy): support for deploy to AWS Elastic Beanstalk
Browse files Browse the repository at this point in the history
  • Loading branch information
Izak88 authored and jkuri committed Dec 12, 2017
1 parent a0225bc commit 4913c2c
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 44 deletions.
3 changes: 3 additions & 0 deletions src/api/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Observable, Observer } from 'rxjs';
import { s3Deploy } from './deploy/aws-s3';
import { codeDeploy } from './deploy/aws-code-deploy';
import { elasticDeploy } from './deploy/aws-elastic';

export function deploy(preferences: any, container: string, variables: string[]): Observable<any> {
return new Observable((observer: Observer<any>) => {
Expand All @@ -23,6 +24,8 @@ function deployProvider(provider, preferences, container, variables): Observable
return s3Deploy(preferences, container, variables);
case 'codeDeploy':
return codeDeploy(preferences, container, variables);
case 'elastic':
return elasticDeploy(preferences, container, variables);
default:
return new Observable((observer: Observer<any>) => {
observer.error({
Expand Down
34 changes: 11 additions & 23 deletions src/api/deploy/aws-code-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,17 @@ export function codeDeploy(
style.bold.close + style.yellow.close + '\r\n';
observer.next({ type: 'data', data: msg });

// 2. install awscli and set credentials
let command = { type: CommandType.deploy, command: 'sudo apt-get install awscli -y' };
// 2. set credentials for awscli
let command = {
type: CommandType.deploy, command: `aws configure set aws_access_key_id ${accessKeyId}`
};
attachExec(container, command)
.toPromise()
.then(result => {
if (!(result && result.data === 0)) {
const msg = `apt-get install awscli failed`;
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
}

command = {
type: CommandType.deploy,
command: `aws configure set aws_access_key_id ${accessKeyId}`
};

return attachExec(container, command).toPromise();
})
.then(result => {
if (!(result && result.data === 0)) {
const msg = 'aws configure aws_access_key_id failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}

command = {
Expand All @@ -121,7 +109,7 @@ export function codeDeploy(
if (!(result && result.data === 0)) {
const msg = 'aws configure aws_secret_access_key failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}

command = {
Expand All @@ -134,7 +122,7 @@ export function codeDeploy(
if (!(result && result.data === 0)) {
const msg = 'aws configure region failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}

// 3. check if deployment-group exists (otherwise create it)
Expand All @@ -155,15 +143,15 @@ export function codeDeploy(
if (!(result && result.data === 0)) {
const msg = 'create-deployment-group failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}

Promise.resolve();
});
} else {
const msg = `deployment group doesn't exists and arn parameter is empty`;
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}
} else {
Promise.resolve();
Expand Down Expand Up @@ -191,7 +179,7 @@ export function codeDeploy(
const msg = 'ApplicationStore can only be s3 or github,'
+ ' other stores are not supported';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}

return attachExec(container, command)
Expand All @@ -200,7 +188,7 @@ export function codeDeploy(
if (!(result && result.data === 0)) {
const msg = 'create-deployment failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(-1);
return Promise.reject(1);
}

Promise.resolve();
Expand Down
252 changes: 252 additions & 0 deletions src/api/deploy/aws-elastic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import { Observable, Observer } from 'rxjs';
import { attachExec } from '../docker';
import { CommandType } from '../config';
import { findFromEnvVariables } from '../deploy';
import * as style from 'ansi-styles';
import chalk from 'chalk';

export function elasticDeploy(
preferences: any, container: string, variables: string[]
): Observable<any> {
return new Observable((observer: Observer<any>) => {
// 1. check preferences
const application = preferences.application;
let accessKeyId = findFromEnvVariables(variables, 'accessKeyId');
let secretAccessKey = findFromEnvVariables(variables, 'secretAccessKey');
let region = findFromEnvVariables(variables, 'region');
let version = findFromEnvVariables(variables, 'version');
let description = preferences.description;
let s3Bucket = preferences.s3Bucket;
let codeCommit = preferences.codeCommit;
let applicationType = preferences.applicationType;
let environmentName = preferences.environmentName;
let solutionStackName = preferences.solutionStackName;
let environmentTemplate = preferences.environmentTemplate;
let errors = false;

if (!application) {
const msg = chalk.red('application is not set in yml config file \r\n');
observer.next({ type: 'data', data: msg});
errors = true;
}

if (!environmentName) {
const msg = chalk.red('environmentName is not set in yml config file \r\n');
observer.next({ type: 'data', data: msg});
errors = true;
}

if (!accessKeyId) {
if (preferences && preferences.accessKeyId) {
accessKeyId = preferences.accessKeyId;
} else {
const msg = chalk.red('accessKeyId is not set in environment '
+ 'variables or in yml config \r\n');
observer.next({ type: 'data', data: msg});
errors = true;
}
}

if (!secretAccessKey) {
if (preferences && preferences.secretAccessKey) {
secretAccessKey = preferences.secretAccessKey;
} else {
const msg = chalk.red('secretAccessKey is not set in environment variables or'
+ ' in yml config \r\n');
observer.next({ type: 'data', data: msg});
errors = true;
}
}

if (!region) {
if (preferences && preferences.region) {
region = preferences.region;
} else {
const msg =
chalk.red('region is not set in environment variables or in yml config file \r\n');
observer.next({ type: 'data', data: msg});
errors = true;
}
}

let sourceRepository;
let sourceLocation;
if (s3Bucket && codeCommit) {
const msg = chalk.red('Specify a source bundle in S3 bucket or a commit in an AWS CodeCommit'
+ 'repository, but not both. \r\n');
observer.next({ type: 'data', data: msg});
errors = true;
} else if (s3Bucket) {
sourceRepository = 'S3';
sourceLocation = s3Bucket;
} else if (codeCommit) {
sourceRepository = 'CodeCommit';
sourceLocation = codeCommit;
}

if (!errors) {
if (!version) {
const date = (new Date()).toLocaleDateString();
version = `abstruse_${date}`;
}

if (!description) {
description = 'Deployed with Abstruse.';
}

if (!applicationType) {
applicationType = 'zip';
}

let msg = style.yellow.open + style.bold.open + '==> deploy started' +
style.bold.close + style.yellow.close + '\r\n';
observer.next({ type: 'data', data: msg });

// 2. set credentials for awscli
let command = {
type: CommandType.deploy, command: `aws configure set aws_access_key_id ${accessKeyId}`
};
attachExec(container, command)
.toPromise()
.then(result => {
if (!(result && result.data === 0)) {
const msg = 'aws configure aws_access_key_id failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(1);
}

let command = {
type: CommandType.deploy,
command: `aws configure set aws_secret_access_key ${secretAccessKey}`
};

return attachExec(container, command).toPromise();
})
.then(result => {
if (!(result && result.data === 0)) {
const msg = 'aws configure aws_secret_access_key failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(1);
}

let command = {
type: CommandType.deploy, command: `aws configure set region ${region}`
};

return attachExec(container, command).toPromise();
})
.then(result => {
if (!(result && result.data === 0)) {
const msg = 'aws configure region failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(1);
}

// 3. create-application-version
let command;
if (s3Bucket || codeCommit) {
command = {
type: CommandType.deploy, command: `aws elasticbeanstalk create-application-version`
+ ` --application-name "${application}" --version-label "${version}"`
+ ` --description "${description}" --source-build-information`
+ ` SourceType="${applicationType}", SourceRepository="${sourceRepository}",`
+ ` SourceLocation="${sourceLocation}" --auto-create-application`
};
} else {
command = {
type: CommandType.deploy, command: `aws elasticbeanstalk create-application-version`
+ ` --application-name "${application}" --version-label "${version}"`
+ ` --description "${description}" --auto-create-application`
};
}

return attachExec(container, command).toPromise();
})
.then(() => {
// 3. check if environment exists
return environmentExists(container, environmentName);
})
.then(exists => {
if (exists) {
// 4. create-environment
if (environmentTemplate) {
let command = {
type: CommandType.deploy, command: `aws elasticbeanstalk create-environment`
+ ` --application-name "${application}" --environment-name "${environmentName}"`
+ ` --template-name "${environmentTemplate}"`
};

return attachExec(container, command)
.toPromise()
.then(result => {
if (!(result && result.data === 0)) {
const msg = 'aws create environment failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(1);
}

return Promise.resolve();
});
} else if (solutionStackName) {
let command = {
type: CommandType.deploy, command: `aws elasticbeanstalk create-environment`
+ ` --application-name "${application}" --environment-name "${environmentName}"`
+ ` --solution-stack-name "${solutionStackName}"`
};

return attachExec(container, command)
.toPromise()
.then(result => {
if (!(result && result.data === 0)) {
const msg = 'aws create environment failed';
observer.next({ type: 'containerError', data: msg});
return Promise.reject(1);
}

return Promise.resolve();
});
}

const msg = `Environment with name ${environmentName} doesn't exists, environment `
+ `template-name or solution-stack-name has to be provided in deploy configuration`
+ ` to successfully create new environment`;
observer.next({ type: 'containerError', data: msg});
return Promise.reject(1);
} else {
return Promise.resolve();
}
})
.then(() => {
let msg = style.yellow.open + style.bold.open + '==> deployment completed successfully!'
+ style.bold.close + style.yellow.close + '\r\n';
observer.next({ type: 'data', data: msg });
observer.complete();
})
.catch(err => {
observer.error(err);
observer.complete();
});
} else {
observer.error(1);
observer.complete();
}
});
}

function environmentExists(container, environment): Promise<any> {
return new Promise((resolve, reject) => {
const getEnvCommand = `aws elasticbeanstalk describe-environments --environment-names`
+ ` "${environment}"`;
let envExists = false;
attachExec(container, { type: CommandType.deploy, command: getEnvCommand })
.subscribe(event => {
if (event && event.data) {
if (String(event.data).indexOf(environment) != -1) {
envExists = true;
}
}
},
err => reject(err),
() => resolve(envExists));
});
}
Loading

0 comments on commit 4913c2c

Please sign in to comment.