Skip to content

Commit 4913c2c

Browse files
Izak88jkuri
authored andcommitted
feat(deploy): support for deploy to AWS Elastic Beanstalk
1 parent a0225bc commit 4913c2c

File tree

5 files changed

+276
-44
lines changed

5 files changed

+276
-44
lines changed

src/api/deploy.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Observable, Observer } from 'rxjs';
22
import { s3Deploy } from './deploy/aws-s3';
33
import { codeDeploy } from './deploy/aws-code-deploy';
4+
import { elasticDeploy } from './deploy/aws-elastic';
45

56
export function deploy(preferences: any, container: string, variables: string[]): Observable<any> {
67
return new Observable((observer: Observer<any>) => {
@@ -23,6 +24,8 @@ function deployProvider(provider, preferences, container, variables): Observable
2324
return s3Deploy(preferences, container, variables);
2425
case 'codeDeploy':
2526
return codeDeploy(preferences, container, variables);
27+
case 'elastic':
28+
return elasticDeploy(preferences, container, variables);
2629
default:
2730
return new Observable((observer: Observer<any>) => {
2831
observer.error({

src/api/deploy/aws-code-deploy.ts

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,29 +85,17 @@ export function codeDeploy(
8585
style.bold.close + style.yellow.close + '\r\n';
8686
observer.next({ type: 'data', data: msg });
8787

88-
// 2. install awscli and set credentials
89-
let command = { type: CommandType.deploy, command: 'sudo apt-get install awscli -y' };
88+
// 2. set credentials for awscli
89+
let command = {
90+
type: CommandType.deploy, command: `aws configure set aws_access_key_id ${accessKeyId}`
91+
};
9092
attachExec(container, command)
9193
.toPromise()
92-
.then(result => {
93-
if (!(result && result.data === 0)) {
94-
const msg = `apt-get install awscli failed`;
95-
observer.next({ type: 'containerError', data: msg});
96-
return Promise.reject(-1);
97-
}
98-
99-
command = {
100-
type: CommandType.deploy,
101-
command: `aws configure set aws_access_key_id ${accessKeyId}`
102-
};
103-
104-
return attachExec(container, command).toPromise();
105-
})
10694
.then(result => {
10795
if (!(result && result.data === 0)) {
10896
const msg = 'aws configure aws_access_key_id failed';
10997
observer.next({ type: 'containerError', data: msg});
110-
return Promise.reject(-1);
98+
return Promise.reject(1);
11199
}
112100

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

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

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

161149
Promise.resolve();
162150
});
163151
} else {
164152
const msg = `deployment group doesn't exists and arn parameter is empty`;
165153
observer.next({ type: 'containerError', data: msg});
166-
return Promise.reject(-1);
154+
return Promise.reject(1);
167155
}
168156
} else {
169157
Promise.resolve();
@@ -191,7 +179,7 @@ export function codeDeploy(
191179
const msg = 'ApplicationStore can only be s3 or github,'
192180
+ ' other stores are not supported';
193181
observer.next({ type: 'containerError', data: msg});
194-
return Promise.reject(-1);
182+
return Promise.reject(1);
195183
}
196184

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

206194
Promise.resolve();

src/api/deploy/aws-elastic.ts

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import { Observable, Observer } from 'rxjs';
2+
import { attachExec } from '../docker';
3+
import { CommandType } from '../config';
4+
import { findFromEnvVariables } from '../deploy';
5+
import * as style from 'ansi-styles';
6+
import chalk from 'chalk';
7+
8+
export function elasticDeploy(
9+
preferences: any, container: string, variables: string[]
10+
): Observable<any> {
11+
return new Observable((observer: Observer<any>) => {
12+
// 1. check preferences
13+
const application = preferences.application;
14+
let accessKeyId = findFromEnvVariables(variables, 'accessKeyId');
15+
let secretAccessKey = findFromEnvVariables(variables, 'secretAccessKey');
16+
let region = findFromEnvVariables(variables, 'region');
17+
let version = findFromEnvVariables(variables, 'version');
18+
let description = preferences.description;
19+
let s3Bucket = preferences.s3Bucket;
20+
let codeCommit = preferences.codeCommit;
21+
let applicationType = preferences.applicationType;
22+
let environmentName = preferences.environmentName;
23+
let solutionStackName = preferences.solutionStackName;
24+
let environmentTemplate = preferences.environmentTemplate;
25+
let errors = false;
26+
27+
if (!application) {
28+
const msg = chalk.red('application is not set in yml config file \r\n');
29+
observer.next({ type: 'data', data: msg});
30+
errors = true;
31+
}
32+
33+
if (!environmentName) {
34+
const msg = chalk.red('environmentName is not set in yml config file \r\n');
35+
observer.next({ type: 'data', data: msg});
36+
errors = true;
37+
}
38+
39+
if (!accessKeyId) {
40+
if (preferences && preferences.accessKeyId) {
41+
accessKeyId = preferences.accessKeyId;
42+
} else {
43+
const msg = chalk.red('accessKeyId is not set in environment '
44+
+ 'variables or in yml config \r\n');
45+
observer.next({ type: 'data', data: msg});
46+
errors = true;
47+
}
48+
}
49+
50+
if (!secretAccessKey) {
51+
if (preferences && preferences.secretAccessKey) {
52+
secretAccessKey = preferences.secretAccessKey;
53+
} else {
54+
const msg = chalk.red('secretAccessKey is not set in environment variables or'
55+
+ ' in yml config \r\n');
56+
observer.next({ type: 'data', data: msg});
57+
errors = true;
58+
}
59+
}
60+
61+
if (!region) {
62+
if (preferences && preferences.region) {
63+
region = preferences.region;
64+
} else {
65+
const msg =
66+
chalk.red('region is not set in environment variables or in yml config file \r\n');
67+
observer.next({ type: 'data', data: msg});
68+
errors = true;
69+
}
70+
}
71+
72+
let sourceRepository;
73+
let sourceLocation;
74+
if (s3Bucket && codeCommit) {
75+
const msg = chalk.red('Specify a source bundle in S3 bucket or a commit in an AWS CodeCommit'
76+
+ 'repository, but not both. \r\n');
77+
observer.next({ type: 'data', data: msg});
78+
errors = true;
79+
} else if (s3Bucket) {
80+
sourceRepository = 'S3';
81+
sourceLocation = s3Bucket;
82+
} else if (codeCommit) {
83+
sourceRepository = 'CodeCommit';
84+
sourceLocation = codeCommit;
85+
}
86+
87+
if (!errors) {
88+
if (!version) {
89+
const date = (new Date()).toLocaleDateString();
90+
version = `abstruse_${date}`;
91+
}
92+
93+
if (!description) {
94+
description = 'Deployed with Abstruse.';
95+
}
96+
97+
if (!applicationType) {
98+
applicationType = 'zip';
99+
}
100+
101+
let msg = style.yellow.open + style.bold.open + '==> deploy started' +
102+
style.bold.close + style.yellow.close + '\r\n';
103+
observer.next({ type: 'data', data: msg });
104+
105+
// 2. set credentials for awscli
106+
let command = {
107+
type: CommandType.deploy, command: `aws configure set aws_access_key_id ${accessKeyId}`
108+
};
109+
attachExec(container, command)
110+
.toPromise()
111+
.then(result => {
112+
if (!(result && result.data === 0)) {
113+
const msg = 'aws configure aws_access_key_id failed';
114+
observer.next({ type: 'containerError', data: msg});
115+
return Promise.reject(1);
116+
}
117+
118+
let command = {
119+
type: CommandType.deploy,
120+
command: `aws configure set aws_secret_access_key ${secretAccessKey}`
121+
};
122+
123+
return attachExec(container, command).toPromise();
124+
})
125+
.then(result => {
126+
if (!(result && result.data === 0)) {
127+
const msg = 'aws configure aws_secret_access_key failed';
128+
observer.next({ type: 'containerError', data: msg});
129+
return Promise.reject(1);
130+
}
131+
132+
let command = {
133+
type: CommandType.deploy, command: `aws configure set region ${region}`
134+
};
135+
136+
return attachExec(container, command).toPromise();
137+
})
138+
.then(result => {
139+
if (!(result && result.data === 0)) {
140+
const msg = 'aws configure region failed';
141+
observer.next({ type: 'containerError', data: msg});
142+
return Promise.reject(1);
143+
}
144+
145+
// 3. create-application-version
146+
let command;
147+
if (s3Bucket || codeCommit) {
148+
command = {
149+
type: CommandType.deploy, command: `aws elasticbeanstalk create-application-version`
150+
+ ` --application-name "${application}" --version-label "${version}"`
151+
+ ` --description "${description}" --source-build-information`
152+
+ ` SourceType="${applicationType}", SourceRepository="${sourceRepository}",`
153+
+ ` SourceLocation="${sourceLocation}" --auto-create-application`
154+
};
155+
} else {
156+
command = {
157+
type: CommandType.deploy, command: `aws elasticbeanstalk create-application-version`
158+
+ ` --application-name "${application}" --version-label "${version}"`
159+
+ ` --description "${description}" --auto-create-application`
160+
};
161+
}
162+
163+
return attachExec(container, command).toPromise();
164+
})
165+
.then(() => {
166+
// 3. check if environment exists
167+
return environmentExists(container, environmentName);
168+
})
169+
.then(exists => {
170+
if (exists) {
171+
// 4. create-environment
172+
if (environmentTemplate) {
173+
let command = {
174+
type: CommandType.deploy, command: `aws elasticbeanstalk create-environment`
175+
+ ` --application-name "${application}" --environment-name "${environmentName}"`
176+
+ ` --template-name "${environmentTemplate}"`
177+
};
178+
179+
return attachExec(container, command)
180+
.toPromise()
181+
.then(result => {
182+
if (!(result && result.data === 0)) {
183+
const msg = 'aws create environment failed';
184+
observer.next({ type: 'containerError', data: msg});
185+
return Promise.reject(1);
186+
}
187+
188+
return Promise.resolve();
189+
});
190+
} else if (solutionStackName) {
191+
let command = {
192+
type: CommandType.deploy, command: `aws elasticbeanstalk create-environment`
193+
+ ` --application-name "${application}" --environment-name "${environmentName}"`
194+
+ ` --solution-stack-name "${solutionStackName}"`
195+
};
196+
197+
return attachExec(container, command)
198+
.toPromise()
199+
.then(result => {
200+
if (!(result && result.data === 0)) {
201+
const msg = 'aws create environment failed';
202+
observer.next({ type: 'containerError', data: msg});
203+
return Promise.reject(1);
204+
}
205+
206+
return Promise.resolve();
207+
});
208+
}
209+
210+
const msg = `Environment with name ${environmentName} doesn't exists, environment `
211+
+ `template-name or solution-stack-name has to be provided in deploy configuration`
212+
+ ` to successfully create new environment`;
213+
observer.next({ type: 'containerError', data: msg});
214+
return Promise.reject(1);
215+
} else {
216+
return Promise.resolve();
217+
}
218+
})
219+
.then(() => {
220+
let msg = style.yellow.open + style.bold.open + '==> deployment completed successfully!'
221+
+ style.bold.close + style.yellow.close + '\r\n';
222+
observer.next({ type: 'data', data: msg });
223+
observer.complete();
224+
})
225+
.catch(err => {
226+
observer.error(err);
227+
observer.complete();
228+
});
229+
} else {
230+
observer.error(1);
231+
observer.complete();
232+
}
233+
});
234+
}
235+
236+
function environmentExists(container, environment): Promise<any> {
237+
return new Promise((resolve, reject) => {
238+
const getEnvCommand = `aws elasticbeanstalk describe-environments --environment-names`
239+
+ ` "${environment}"`;
240+
let envExists = false;
241+
attachExec(container, { type: CommandType.deploy, command: getEnvCommand })
242+
.subscribe(event => {
243+
if (event && event.data) {
244+
if (String(event.data).indexOf(environment) != -1) {
245+
envExists = true;
246+
}
247+
}
248+
},
249+
err => reject(err),
250+
() => resolve(envExists));
251+
});
252+
}

0 commit comments

Comments
 (0)