Skip to content

Commit

Permalink
Merge pull request #1 from ErwinBustillo/module-2-s3-cloudfront-serve…
Browse files Browse the repository at this point in the history
…less

TASK 2
  • Loading branch information
Erwin Bustillo authored Feb 26, 2023
2 parents c7fa797 + d3f86a7 commit 9ea7157
Show file tree
Hide file tree
Showing 7 changed files with 6,372 additions and 222 deletions.
145 changes: 145 additions & 0 deletions .serverless_plugins/serverless-single-page-app-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use strict";

const spawnSync = require("child_process").spawnSync;

class ServerlessPlugin {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.commands = {
syncToS3: {
usage: "Deploys the `app` directory to your bucket",
lifecycleEvents: ["sync"],
},
domainInfo: {
usage: "Fetches and prints out the deployed CloudFront domain names",
lifecycleEvents: ["domainInfo"],
},
invalidateCloudFrontCache: {
usage: "Invalidates CloudFront cache",
lifecycleEvents: ["invalidateCache"],
},
};

this.hooks = {
"syncToS3:sync": this.syncDirectory.bind(this),
"domainInfo:domainInfo": this.domainInfo.bind(this),
"invalidateCloudFrontCache:invalidateCache":
this.invalidateCache.bind(this),
};
}

runAwsCommand(args) {
let command = "aws";
if (this.serverless.variables.service.provider.region) {
command = `${command} --region ${this.serverless.variables.service.provider.region}`;
}
if (this.serverless.variables.service.provider.profile) {
command = `${command} --profile ${this.serverless.variables.service.provider.profile}`;
}
const result = spawnSync(command, args, { shell: true });
const stdout = result.stdout.toString();
const sterr = result.stderr.toString();
if (stdout) {
this.serverless.cli.log(stdout);
}
if (sterr) {
this.serverless.cli.log(sterr);
}

return { stdout, sterr };
}

// syncs the `app` directory to the provided bucket
syncDirectory() {
const s3Bucket = this.serverless.variables.service.custom.s3Bucket;
const buildFolder =
this.serverless.variables.service.custom.client.distributionFolder;
const args = [
"s3",
"sync",
`${buildFolder}/`,
`s3://${s3Bucket}/`,
"--delete",
];
const { sterr } = this.runAwsCommand(args);
if (!sterr) {
this.serverless.cli.log("Successfully synced to the S3 bucket");
} else {
throw new Error("Failed syncing to the S3 bucket");
}
}

// fetches the domain name from the CloudFront outputs and prints it out
async domainInfo() {
const provider = this.serverless.getProvider("aws");
const stackName = provider.naming.getStackName(this.options.stage);
const result = await provider.request(
"CloudFormation",
"describeStacks",
{ StackName: stackName },
this.options.stage,
this.options.region
);

const outputs = result.Stacks[0].Outputs;
const output = outputs.find(
(entry) => entry.OutputKey === "WebAppCloudFrontDistributionOutput"
);

if (output && output.OutputValue) {
this.serverless.cli.log(`Web App Domain: ${output.OutputValue}`);
return output.OutputValue;
}

this.serverless.cli.log("Web App Domain: Not Found");
const error = new Error("Could not extract Web App Domain");
throw error;
}

async invalidateCache() {
const provider = this.serverless.getProvider("aws");

const domain = await this.domainInfo();

const result = await provider.request(
"CloudFront",
"listDistributions",
{},
this.options.stage,
this.options.region
);

const distributions = result.DistributionList.Items;
const distribution = distributions.find(
(entry) => entry.DomainName === domain
);

if (distribution) {
this.serverless.cli.log(
`Invalidating CloudFront distribution with id: ${distribution.Id}`
);
const args = [
"cloudfront",
"create-invalidation",
"--distribution-id",
distribution.Id,
"--paths",
'"/*"',
];
const { sterr } = this.runAwsCommand(args);
if (!sterr) {
this.serverless.cli.log("Successfully invalidated CloudFront cache");
} else {
throw new Error("Failed invalidating CloudFront cache");
}
} else {
const message = `Could not find distribution with domain ${domain}`;
const error = new Error(message);
this.serverless.cli.log(message);
throw error;
}
}
}

module.exports = ServerlessPlugin;
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,42 @@ Follow the steps:
- git clone
- npm i
- npm start

## Serverless scripts
### `client:deploy`, `client:deploy:nc`

Deploy the project build from `dist` folder to configured in `serverless.yml` AWS S3 bucket with or without confirmation.

### `client:build:deploy`, `client:build:deploy:nc`

Combination of `build` and `client:deploy` commands with or without confirmation.

### `cloudfront:setup`

Deploy configured in `serverless.yml` stack via CloudFormation.

### `cloudfront:domainInfo`

Display cloudfront domain information in console.

### `cloudfront:invalidateCache`

Invalidate cloudfront cache.

### `cloudfront:build:deploy`, `cloudfront:build:deploy:nc`

Combination of `client:build:deploy` and `cloudfront:invalidateCache` commands with or without confirmation.

### `cloudfront:update:build:deploy`, `cloudfront:update:build:deploy:nc`

Combination of `cloudfront:setup` and `cloudfront:build:deploy` commands with or without confirmation.

### `serverless:remove`

Remove an entire stack configured in `serverless.yml` via CloudFormation.

## Links

[S3](http://eshop-gaming-bucket.s3-website-us-east-1.amazonaws.com/)

[CLOUDFRONT](https://d3p9c88204whqm.cloudfront.net/)
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/app",
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
Expand Down
Loading

0 comments on commit 9ea7157

Please sign in to comment.