Skip to content

Commit

Permalink
Inject environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli committed Aug 27, 2020
1 parent 6e206e0 commit c56dc27
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 17 deletions.
11 changes: 10 additions & 1 deletion demo/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@ class ServerlessPlugin {
setEnvironmentVariables() {
this.serverless.service.provider.environment = this.serverless.service.provider.environment || {};

this.serverless.service.provider.environment['FOO'] = 'BAR';
const json = child_process.execSync('bin/run variables');
const variables = JSON.parse(json.toString());

Object.keys(variables).map(name => {
if (name in this.serverless.service.provider.environment) {
// Avoid overwriting an existing variable
return;
}
this.serverless.service.provider.environment[name] = variables[name];
});
}

setPermissions() {
Expand Down
1 change: 0 additions & 1 deletion resources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ s3:
# cors: true

#db:
# db:

static-website:
6 changes: 2 additions & 4 deletions src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ export class Config {
}
}
if (template.hasOwnProperty('db')) {
for (const [key, value] of Object.entries(template.db)) {
stack.add(new Database(key, value as Record<string, any>));
}
stack.add(new Database(stack.name, template.db as Record<string, any>));
}
if (template.hasOwnProperty('static-website')) {
stack.add(new StaticWebsite(template['static-website']));
stack.add(new StaticWebsite(stack.name, template['static-website']));
}

return stack;
Expand Down
11 changes: 11 additions & 0 deletions src/Stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,15 @@ export class Stack {
});
return permissions;
}

variables() {
const variables: Record<string, any> = {};
this.components.map(component => {
const newVariables = component.envVariables();
Object.keys(newVariables).map(name => {
variables[name] = newVariables[name];
});
});
return variables;
}
}
12 changes: 12 additions & 0 deletions src/commands/variables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Command} from '@oclif/command'
import {Config} from "../Config";

export default class Variables extends Command {
static description = 'export the environment variables'

async run() {
const stack = (new Config).getStack();

this.log(JSON.stringify(stack.variables(), undefined, 2));
}
}
6 changes: 6 additions & 0 deletions src/components/Component.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import {pascalCase} from "pascal-case";
import {pascalCaseTransformMerge} from "pascal-case";
import {constantCase} from "constant-case";
import {PolicyStatement} from "../utils/cloudformation";

export abstract class Component {
abstract compile(): Record<string, any>;
abstract outputs(): Record<string, any>;
abstract permissions(): PolicyStatement[];
abstract envVariables(): Record<string, any>;

formatResourceName(name: string): string {
return pascalCase(name, {
transform: pascalCaseTransformMerge,
});
}

formatEnvVariableName(name: string): string {
return constantCase(name);
}

fnRef(resource: string): object {
return { Ref: resource };
}
Expand Down
30 changes: 24 additions & 6 deletions src/components/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import {Component} from "./Component";
import {PolicyStatement} from "../utils/cloudformation";

export class Database extends Component {
private name: string;
private stackName: string;
private props: Record<string, any>;
private dbResourceName: string;

constructor(name: string, props: Record<string, any> | null) {
constructor(stackName: string, props: Record<string, any> | null) {
super();
this.name = name;
this.stackName = stackName;
this.props = props ? props : {};

this.dbResourceName = this.formatResourceName(this.name);
this.dbResourceName = this.formatResourceName('Database');
}

compile(): Record<string, any> {
Expand All @@ -34,11 +34,11 @@ export class Database extends Component {
const db: any = {
Type: 'AWS::RDS::DBInstance',
Properties: {
DBName: this.name,
DBName: this.stackName,
Engine: engine,
MasterUsername: 'admin',
MasterUserPassword: 'password',
DBInstanceIdentifier: this.name,
DBInstanceIdentifier: this.stackName,
DBInstanceClass: 'db.t3.micro',
StorageType: 'gp2',
AllocatedStorage: '20', // minimum is 20 GB
Expand All @@ -55,15 +55,33 @@ export class Database extends Component {
[this.dbResourceName + 'Host']: {
Description: 'Hostname of the database.',
Value: this.fnGetAtt(this.dbResourceName, 'Endpoint.Address'),
Export: {
Name: this.stackName + '-' + this.dbResourceName + '-Host',
},
},
[this.dbResourceName + 'Port']: {
Description: 'Port of the database.',
Value: this.fnGetAtt(this.dbResourceName, 'Endpoint.Port'),
Export: {
Name: this.stackName + '-' + this.dbResourceName + '-Port',
},
},
};
}

permissions(): PolicyStatement[] {
return [];
}

envVariables() {
let variables: Record<string, any> = {};

const dbHost = this.fnImportValue(this.stackName + '-' + this.dbResourceName + '-Host');
variables[this.formatEnvVariableName(this.dbResourceName + '_HOST')] = dbHost;

const dbPort = this.fnImportValue(this.stackName + '-' + this.dbResourceName + '-Port');
variables[this.formatEnvVariableName(this.dbResourceName + '_PORT')] = dbPort;

return variables;
}
}
15 changes: 11 additions & 4 deletions src/components/S3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ import {PolicyStatement} from "../utils/cloudformation";

export class S3 extends Component {
private stackName: string;
private name: string;
private bucketName: string;
private props: Record<string, any>;
private bucketResourceName: string;

constructor(stackName: string, name: string, props: Record<string, any> | null) {
super();
this.stackName = stackName;
this.name = name;
this.bucketName = name;
this.props = props ? props : {};

this.bucketResourceName = this.formatResourceName(this.name);
this.bucketResourceName = this.formatResourceName(this.bucketName);
}

compile(): Record<string, any> {
const bucket: any = {
Type: 'AWS::S3::Bucket',
Properties: {
BucketName: this.name,
BucketName: this.bucketName,
},
};

Expand Down Expand Up @@ -90,4 +90,11 @@ export class S3 extends Component {
]),
];
}

envVariables() {
const variableName = this.formatEnvVariableName('BUCKET_' + this.bucketName);
return {
[variableName]: this.bucketName,
};
}
}
30 changes: 29 additions & 1 deletion src/components/StaticWebsite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import {Component} from "./Component";
import {PolicyStatement} from "../utils/cloudformation";

export class StaticWebsite extends Component {
private stackName: string;
private props: Record<string, any>;
private bucketResourceName: string;

constructor(props: Record<string, any> | null) {
constructor(stackName: string, props: Record<string, any> | null) {
super();
this.stackName = stackName;
this.props = props ? props : {};

this.bucketResourceName = this.formatResourceName('StaticWebsite');
Expand Down Expand Up @@ -128,15 +130,41 @@ export class StaticWebsite extends Component {
[this.bucketResourceName + 'Bucket']: {
Description: 'Name of the bucket that stores the static website.',
Value: this.fnRef(this.bucketResourceName),
Export: {
Name: this.stackName + '-StaticWebsite-BucketName',
},
},
CloudFrontDomain: {
Description: 'CloudFront domain name.',
Value: this.fnGetAtt('WebsiteCDN', 'DomainName'),
Export: {
Name: this.stackName + '-StaticWebsite-CloudFrontDomain',
},
},
};
}

permissions(): PolicyStatement[] {
return [];
}

envVariables() {
let variables: Record<string, any> = {};

// Bucket name
const bucketName = this.fnImportValue(this.stackName + '-StaticWebsite-BucketName');
variables[this.formatEnvVariableName('STATIC_WEBSITE_BUCKET')] = bucketName;

// Domain
if (this.props.domain) {
variables['STATIC_WEBSITE_DOMAIN'] = this.props.domain;
variables['STATIC_WEBSITE_URL'] = 'https://' + this.props.domain;
} else {
const cloudFrontDomain = this.fnImportValue(this.stackName + '-StaticWebsite-CloudFrontDomain');
variables['STATIC_WEBSITE_DOMAIN'] = cloudFrontDomain;
variables['STATIC_WEBSITE_URL'] = this.fnJoin('', ['https://', cloudFrontDomain]);
}

return variables;
}
}

0 comments on commit c56dc27

Please sign in to comment.