Skip to content

Commit

Permalink
feat(infrastructure): add deploy:dev task to infrastructure projects …
Browse files Browse the repository at this point in the history
…for cdk hotswap deployment

Adds a `deploy:dev` task to the infrastructure projects which perform a CDK hotswap deployment
if possible in order to reduce dev cycle times.

Given the dev cycle time reduction we consider this a better option than a local dev server,
since local dev server requires managing credentials locally, as well as things like
environment variables for pointing to other resources (eg ddb tables).

Fixes #599
  • Loading branch information
cogwirrel committed Nov 2, 2023
1 parent cc1ddca commit 5fa3916
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 42 deletions.
4 changes: 2 additions & 2 deletions docs/content/getting_started/shopping_list_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ We are now ready to deploy our API. To do so, run the following steps:
```bash
pdk build
cd packages/infra
pdk run deploy --require-approval never
pdk deploy:dev
```

Once the deployment completes, we can test our API by navigating the the website (either via Cloudfront or locally) and trying out the API Explorer.
Expand Down Expand Up @@ -1192,7 +1192,7 @@ If you are happy with your website locally, you can go ahead and deploy it to AW
```bash
pdk build
cd packages/infra
pdk run deploy --require-approval never
pdk deploy:dev
```

Once the deployment completes, navigate to your cloudfront URL to play around with your deployed website.
Expand Down
11 changes: 7 additions & 4 deletions docs/content/getting_started/your_first_aws_pdk_project.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Inspecting the `projenrc` file, we notice that a single construct is instantiate
- Any parameter listed here can be passed in via the `pdk new` command i.e: `--name="some-other-name"`.
- For python, the moduleName defaults to a snake-cased project name.

You will also notice that the `synth()` method is called on this instance at the end of the file. When you run the `pdk` command, this file will be executed by your runtime and will synthesize this instance which will result in all the files that you see in the previous image.
You will also notice that the `synth()` method is called on this instance at the end of the file. When you run the `pdk` command, this file will be executed by your runtime and will synthesize this instance which will result in all the files that you see in the previous image.

!!!info
Whenever you change the `projenrc` file, make sure you run the `pdk` command from the root of your project to ensure your files are synthesized.
Expand Down Expand Up @@ -293,7 +293,7 @@ For more details on these packages, refer to the [Type-Safe API Developer Guide]
Now, lets build our API by running `pdk build` from the root of our monorepo. You will notice that each package in the monorepo is built in dependency order.

!!!tip
If you run the `pdk build` command again without changing any files, you will notice that the build completes in a fraction of the time (1.7s as per below snippet) as it uses [cached results](https://nx.dev/concepts/how-caching-works) and will only re-build packages that have changed since the last time it was built.
If you run the `pdk build` command again without changing any files, you will notice that the build completes in a fraction of the time (1.7s as per below snippet) as it uses [cached results](https://nx.dev/concepts/how-caching-works) and will only re-build packages that have changed since the last time it was built.

```bash
> NX Successfully ran target build for 7 projects
Expand Down Expand Up @@ -738,9 +738,12 @@ We now can deploy our infrastructure by running the following command:

```bash
cd packages/infra
pdk run deploy --require-approval never
pdk deploy:dev
```

!!!note
The `pdk deploy:dev` command attempts a [CDK hotswap deployment](https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/) if possible. In a production setting (for example in a ci/cd pipeline) use the `pdk deploy` command to ensure a full CloudFormation deployment is performed.

Once the deployment completes, you should see an output that resembles the following:

<img src="../assets/images/deployment_results.png" width="600" />
Expand All @@ -756,7 +759,7 @@ In order to log in to your website, you first need to create a Cognito user. By
1. Navigate to the Cognito AWS console within the account you just deployed to.
1. Click on the user pool you just created
1. Click "Create user"
1. In invitation settings, select "Send an email invitation"
1. In invitation settings, select "Send an email invitation"
1. Enter a username
1. Enter an email address
1. In temporary password, select "Generate a password"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ Congratulations! You have successfully deployed a website and api to AWS!

To check out your website, navigate to the distribution link in the CDK deployment output above to view your website.

!!!tip
Use the `pdk deploy:dev` command in your infrastructure package to perform a CDK hotswap deployment for faster development iterations!

## Destroying the deployed resources

Now that you're done creating your first PDK project, destroy your deployed resources to avoid incurring any costs as follows:
Expand Down
50 changes: 50 additions & 0 deletions packages/infrastructure/src/components/infrastructure-commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 */
import { ProjectUtils } from "@aws/monorepo";
import { Component, Project } from "projen";

/**
* Common commands for infrastructure projects
*/
export class InfrastructureCommands extends Component {
/**
* Retrieves an instance of InfrastructureCommands if one is associated to the given project
* @param project project instance
*/
static of(project: Project): InfrastructureCommands | undefined {
return project.components.find((c) =>
ProjectUtils.isNamedInstanceOf(c, InfrastructureCommands)
) as InfrastructureCommands | undefined;
}

/**
* Retrieves an instance of InfrastructureCommands if one is associated to the given project,
* otherwise creates an InfrastructureCommands instance for the project.
* @param project project instance
*/
static ensure(project: Project): InfrastructureCommands {
return (
InfrastructureCommands.of(project) || new InfrastructureCommands(project)
);
}

constructor(project: Project) {
super(project);

// Add a development deployment task which uses hotswap for faster deployments
// See: https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/
const deployDevTask = project.addTask("deploy:dev", {
receiveArgs: true,
description:
"Performs a hotswap CDK deployment, useful for faster development cycles",
});
// --hotswap-fallback falls back to a regular deployment if there are resources which have
// changed that cannot be hotswapped
deployDevTask.exec(
"cdk deploy --hotswap-fallback --require-approval never",
{
receiveArgs: true,
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Mustache from "mustache";
import { SampleFile } from "projen";
import { AwsCdkJavaApp } from "projen/lib/awscdk";
import { AwsCdkJavaAppOptions } from "./aws-cdk-java-app-options";
import { InfrastructureCommands } from "../../components/infrastructure-commands";
import { DEFAULT_STACK_NAME } from "../../consts";

/**
Expand Down Expand Up @@ -65,6 +66,8 @@ export class InfrastructureJavaProject extends AwsCdkJavaApp {
},
});

InfrastructureCommands.ensure(this);

this.pom.addPlugin("org.apache.maven.plugins/[email protected]");
this.pom.addPlugin("org.apache.maven.plugins/[email protected]", {
configuration: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Mustache from "mustache";
import { SampleFile } from "projen";
import { AwsCdkPythonApp } from "projen/lib/awscdk";
import { AwsCdkPythonAppOptions } from "./aws-cdk-py-app-options";
import { InfrastructureCommands } from "../../components/infrastructure-commands";
import { DEFAULT_STACK_NAME } from "../../consts";

/**
Expand Down Expand Up @@ -66,6 +67,8 @@ export class InfrastructurePyProject extends AwsCdkPythonApp {
},
});

InfrastructureCommands.ensure(this);

["pytest@^7", "syrupy@^4"].forEach((devDep) =>
this.addDevDependency(devDep)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SampleFile } from "projen";
import { AwsCdkTypeScriptApp } from "projen/lib/awscdk";
import { NodeProject } from "projen/lib/javascript";
import { AwsCdkTypeScriptAppOptions } from "./aws-cdk-ts-app-options";
import { InfrastructureCommands } from "../../components/infrastructure-commands";
import { DEFAULT_STACK_NAME } from "../../consts";

/**
Expand Down Expand Up @@ -66,6 +67,8 @@ export class InfrastructureTsProject extends AwsCdkTypeScriptApp {
},
});

InfrastructureCommands.ensure(this);

this.addDeps("@aws/pdk");

const srcDir = path.resolve(
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5fa3916

Please sign in to comment.