Skip to content

Commit

Permalink
Feature/postgesql fix (#2)
Browse files Browse the repository at this point in the history
* new aws-containers sub-generator

New AWS sub-generator which uses Amazon ECS to run JHipster monolithic applications.

Closes #6773

* external constants to localised file

Moved constants from index.js into a separate aws-containers generator specific file

* testing and linting errors

* add back missing constants from aws-containers

* address issue where the base.template is not being read correctly

* Pull Requests review fixs

* wording

* Wording

* Use the Spring Cloud Context version from jhipster-dependencies

* Use the version from jhipster-dependencies

* Wording

* Corrected issues with Postgresql database not deploying correctly

* Switched out NLB for ALB

* Split scaling out from performance question

* Added nesting supporting into repo name

* Added stack name checking

* Corrected issues with the path lookup

* Code review feedback

* Fixed linting errors

* Swapped out bootRepackage for bootWar
  • Loading branch information
ggotti authored and carvallegro committed Feb 27, 2018
1 parent df449dd commit 058bc59
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 74 deletions.
12 changes: 8 additions & 4 deletions generators/aws-containers/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const _ = require('lodash');
const chalk = require('chalk');
const databaseTypes = require('jhipster-core').JHipsterDatabaseTypes.Types;

const BaseGenerator = require('../generator-base');
const docker = require('../docker-base');
Expand Down Expand Up @@ -183,6 +184,7 @@ module.exports = class extends BaseGenerator {
askForSubnets: prompts.askForSubnets,
askCloudFormation: prompts.askCloudFormation,
askPerformances: prompts.askPerformances,
askScaling: prompts.askScaling,
retrievePassword() {
const done = this.async;
// Attempts to retrieve a previously set database password from SSM.
Expand Down Expand Up @@ -281,11 +283,13 @@ module.exports = class extends BaseGenerator {
if (this.abort) return;
this.appConfigs.forEach((appConfig) => {
const app = this.aws.apps.find(a => a.baseName === appConfig.baseName);
const postgresqlType = databaseTypes.postgresql;
app.dbType = appConfig.prodDatabaseType;
app.auroraEngine = appConfig.dbType === 'postgresql' ? 'aurora-postgresql' : 'aurora';
app.auroraFamily = appConfig.dbType === 'postgresql' ? 'aurora-postgresql9.6' : 'aurora5.6';
app.auroraClusterParam = appConfig.dbType === 'postgresql' ? 'client_encoding: UTF8' : 'character_set_database: utf8';
app.auroraDbParam = appConfig.dbType === 'postgresql' ? 'check_function_bodies: 0' : 'sql_mode: IGNORE_SPACE';

app.auroraEngine = appConfig.prodDatabaseType === postgresqlType ? 'aurora-postgresql' : 'aurora-mysql';
app.auroraFamily = appConfig.prodDatabaseType === postgresqlType ? 'aurora-postgresql9.6' : 'aurora-mysql5.7';
app.auroraClusterParam = appConfig.prodDatabaseType === postgresqlType ? 'client_encoding: UTF8' : 'character_set_database: utf8';
app.auroraDbParam = appConfig.prodDatabaseType === postgresqlType ? 'check_function_bodies: 0' : 'sql_mode: IGNORE_SPACE';
});
},
springProjectChanges() {
Expand Down
150 changes: 116 additions & 34 deletions generators/aws-containers/prompts.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,65 @@
const _ = require('lodash');
const chalk = require('chalk');
const databaseTypes = require('jhipster-core').JHipsterDatabaseTypes.Types;

const AURORA_DB_PASSORD_REGEX = /^[^@"\/]{8,42}$/; // eslint-disable-line
const CLOUDFORMATION_STACK_NAME = /[a-zA-Z][-a-zA-Z0-9]*/; // eslint-disable-line

const SCALING_TO_CONFIG = {
low: {
fargate: {
taskCount: 1
},
database: {
instances: 1
}
},
medium: {
fargate: {
taskCount: 2
},
database: {
instances: 1
}
},
high: {
fargate: {
taskCount: 4
},
database: {
instances: 2
}
}
};
const PERF_TO_CONFIG = {
low: {
fargate: {
taskCount: 1,
CPU: '1024',
memory: '2GB'
},
database: {
instances: 1,
size: 'db.t2.small'
size: 'db.t2.small',
supportedEngines: [databaseTypes.mariadb, databaseTypes.mysql]
}
},
medium: {
fargate: {
taskCount: 2,
CPU: '2048',
memory: '4GB'
},
database: {
instances: 1,
size: 'db.t2.medium'
size: 'db.t2.medium',
supportedEngines: [databaseTypes.mariadb, databaseTypes.mysql]
}
},
high: {
fargate: {
taskCount: 4,
CPU: '4096',
memory: '16GB'
},
database: {
instances: 2,
size: 'db.r4.large'
size: 'db.r4.large',
supportedEngines: [databaseTypes.mariadb, databaseTypes.mysql, databaseTypes.postgresql]
}
}
};
Expand All @@ -51,6 +77,7 @@ module.exports = {
askRegion,
askCloudFormation,
askPerformances,
askScaling,
askVPC,
askForDBPasswords,
askForSubnets,
Expand Down Expand Up @@ -144,11 +171,10 @@ function askCloudFormation() {
message: 'Please enter your stack\'s name. (must be unique within a region)',
default: this.aws.cloudFormationName || this.baseName,
validate: (input) => {
if (input) {
return true;
if (_.isEmpty(input) || !input.match(CLOUDFORMATION_STACK_NAME)) {
return 'Stack name must contain letters, digits, or hyphens ';
}

return 'Stack\'s name cannot be empty!';
return true;
}
}
];
Expand Down Expand Up @@ -185,8 +211,9 @@ function askPerformances() {
const awsConfig = this.aws.apps.find(a => a.baseName === config.baseName) || { baseName: config.baseName };
return promptPerformance.call(this, config, awsConfig).then((performance) => {
awsConfig.performance = performance;
awsConfig.fargate = PERF_TO_CONFIG[performance].fargate;
awsConfig.database = PERF_TO_CONFIG[performance].database;

awsConfig.fargate = Object.assign({}, awsConfig.fargate, PERF_TO_CONFIG[performance].fargate);
awsConfig.database = Object.assign({}, awsConfig.database, PERF_TO_CONFIG[performance].database);

_.remove(this.aws.apps, a => _.isEqual(a, awsConfig));
this.aws.apps.push(awsConfig);
Expand All @@ -200,32 +227,34 @@ function askPerformances() {
function promptPerformance(config, awsConfig = { performance: 'low' }) {
if (this.abort) return null;

const performanceLevels = _.keys(PERF_TO_CONFIG)
const prodDatabaseType = config.prodDatabaseType;

if (prodDatabaseType === databaseTypes.postgresql) {
this.log(' ⚠️ Postgresql databases are currently only supported by Aurora on high-performance database instances');
}

const performanceLevels = _(PERF_TO_CONFIG).keys()
.map((key) => {
const perf = PERF_TO_CONFIG[key];
return {
name: `${_.startCase(key)} Performance \t ${chalk.green(`Task: ${perf.fargate.CPU} CPU Units, ${perf.fargate.memory} Ram, Count: ${perf.fargate.taskCount}`)}\t ${chalk.yellow(`DB: ${perf.database.instances} Instance, Size: ${perf.database.size}`)}`,
value: key,
short: key
};
});

const isEngineSupported = perf.database.supportedEngines.includes(prodDatabaseType);
if (isEngineSupported) {
return {
name: `${_.startCase(key)} Performance \t ${chalk.green(`Task: ${perf.fargate.CPU} CPU Units, ${perf.fargate.memory} Ram`)}\t ${chalk.yellow(`DB: Size: ${perf.database.size}`)}`,
value: key,
short: key
};
}
return null;
})
.compact()
.value();
const prompts = [
{
type: 'list',
name: 'performance',
message: `${chalk.red(config.baseName)} Please select your performance.`,
message: `${chalk.red(config.baseName)} Please select your performance level`,
choices: performanceLevels,
default: awsConfig.performance,
validate: (input) => {
if (!input) {
return 'You Must choose at least one performance!';
}
if (input !== 'high' && config.prodDatabaseType === 'postgresql') {
return 'Aurora DB for postgresql is limited to the high performance configuration';
}
return true;
}
default: awsConfig.performance
}
];

Expand All @@ -235,6 +264,59 @@ function promptPerformance(config, awsConfig = { performance: 'low' }) {
});
}


/**
* Ask about scaling
*/
function askScaling() {
if (this.abort) return null;
const done = this.async();
const chainPromises = (index) => {
if (index === this.appConfigs.length) {
done();
return null;
}
const config = this.appConfigs[index];
const awsConfig = this.aws.apps.find(a => a.baseName === config.baseName) || { baseName: config.baseName };
return promptScaling.call(this, config, awsConfig).then((scaling) => {
awsConfig.scaling = scaling;
awsConfig.fargate = Object.assign({}, awsConfig.fargate, SCALING_TO_CONFIG[scaling].fargate);
awsConfig.database = Object.assign({}, awsConfig.database, SCALING_TO_CONFIG[scaling].database);

_.remove(this.aws.apps, a => _.isEqual(a, awsConfig));
this.aws.apps.push(awsConfig);
return chainPromises(index + 1);
});
};

return chainPromises(0);
}

function promptScaling(config, awsConfig = { scaling: 'low' }) {
if (this.abort) return null;

const scalingLevels = _(SCALING_TO_CONFIG).keys()
.map((key) => {
const scale = SCALING_TO_CONFIG[key];
return {
name: `${chalk.green(`${_.startCase(key)} Scaling \t\t Number of Tasks: ${scale.fargate.taskCount}`)}\t ${chalk.yellow(`DB Instances: ${scale.database.instances}`)}`,
value: key,
short: key
};
}).value();
const prompts = [
{
type: 'list',
name: 'scaling',
message: `${chalk.red(config.baseName)} Please select your scaling level`,
choices: scalingLevels,
default: awsConfig.scaling
}
];

return this.prompt(prompts).then(props => props.scaling);
}

/**
* Ask user to select target Virtual Private Network
*/
Expand Down
23 changes: 20 additions & 3 deletions generators/aws-containers/templates/_application.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ Resources:
ToPort: 65535
SourceSecurityGroupId: !GetAtt JHipsterInternalSG.GroupId
VpcId: !Ref vpcID
JHipsterInternetSG:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: External JHipster Container Security Group for ALB
VpcId: !Ref vpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: '8080'
ToPort: '8080'
DestinationSecurityGroupId: !Ref JHipsterInternalSG
JHipsterContainerRegistry:
Type: 'AWS::ECR::Repository'
Properties:
Expand Down Expand Up @@ -260,13 +275,15 @@ Resources:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
Scheme: internet-facing
Type: network
Type: application
Subnets: !Ref elbSubnets
SecurityGroups:
- !Ref JHipsterInternetSG
JHIpsterALBTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
Port: 80
Protocol: TCP
Protocol: HTTP
TargetType: ip
VpcId: !Ref vpcID
HealthCheckIntervalSeconds: 30
Expand All @@ -278,7 +295,7 @@ Resources:
TargetGroupArn: !Ref JHIpsterALBTargetGroup
LoadBalancerArn: !Ref JHipsterLoadBalancer
Port: '80'
Protocol: TCP
Protocol: HTTP
JHipsterAppService:
Type: 'AWS::ECS::Service'
DependsOn:
Expand Down
2 changes: 1 addition & 1 deletion generators/aws-containers/templates/_base.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Resources:
Properties:
Parameters:
parentStackName: !Ref AWS::StackName
repositoryName: <%= app.baseName %>
repositoryName: <%= aws.cloudFormationName%>/<%= app.baseName %>
shouldDeployService: !Ref shouldDeployService
databasePassword: !Ref <%= app.baseName %>DBPassword
TemplateURL: !Join [ '', [ 'https://s3.amazonaws.com/',!Ref applicationStackS3Bucket, '/<%= app.baseName %>.template.yml'] ]
Expand Down
25 changes: 0 additions & 25 deletions generators/docker-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,6 @@ module.exports = {
setAppsFolderPaths,
};

/**
* Check Docker
*/
function checkDocker() {
if (this.options['skip-checks']) return;
const done = this.async();

shelljs.exec('docker -v', { silent: true }, (code, stdout, stderr) => {
if (stderr) {
this.log(chalk.red('Docker version 1.10.0 or later is not installed on your computer.\n' +
' Read http://docs.docker.com/engine/installation/#installation\n'));
} else {
const dockerVersion = stdout.split(' ')[2].replace(/,/g, '');
const dockerVersionMajor = dockerVersion.split('.')[0];
const dockerVersionMinor = dockerVersion.split('.')[1];
if (dockerVersionMajor < 1 || (dockerVersionMajor === 1 && dockerVersionMinor < 10)) {
this.log(chalk.red(`${'Docker version 1.10.0 or later is not installed on your computer.\n' +
' Docker version found: '}${dockerVersion}\n` +
' Read http://docs.docker.com/engine/installation/#installation\n'));
}
}
done();
});
}

/**
* Check Images
*/
Expand Down
2 changes: 1 addition & 1 deletion generators/docker-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function checkImageExist(opts = { cwd: './', appConfig: null }) {
this.dockerBuildCommand = './mvnw verify -Pprod dockerfile:build';
} else {
imagePath = this.destinationPath(`${opts.cwd + opts.cwd}/build/docker`);
this.dockerBuildCommand = './gradlew -Pprod bootRepackage buildDocker';
this.dockerBuildCommand = './gradlew -Pprod bootWar buildDocker';
}

if (shelljs.ls(imagePath).length === 0) {
Expand Down
Loading

0 comments on commit 058bc59

Please sign in to comment.