From eb96a68c8087da1613c0e021f6ea0ba1afa708f7 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 30 May 2018 15:49:04 +0200 Subject: [PATCH] Big docs update - Changed wording, add more references between sections. - Highlighted some details better. - Reorganized discussion about L1s and L2s a bit to make it flow better. - Changes the title of the section that used to be called "using l2s" into "writing CDK apps". - Update formatting. --- packages/docs/build-docs.sh | 10 - packages/docs/package.json | 18 +- packages/docs/src/cloudformation.rst | 403 +++++++----------- packages/docs/src/concepts.rst | 178 ++++---- packages/docs/src/conf.py | 4 + packages/docs/src/creating-constructs.rst | 30 ++ packages/docs/src/examples.rst | 2 +- packages/docs/src/guidelines.rst | 43 +- packages/docs/src/index.rst | 3 +- packages/docs/src/l1-vs-l2.rst | 117 +++++ packages/docs/src/tools.rst | 32 +- ...ng-constructs.rst => writing-cdk-apps.rst} | 67 ++- 12 files changed, 522 insertions(+), 385 deletions(-) create mode 100644 packages/docs/src/l1-vs-l2.rst rename packages/docs/src/{using-constructs.rst => writing-cdk-apps.rst} (58%) diff --git a/packages/docs/build-docs.sh b/packages/docs/build-docs.sh index f51a0130024ae..0905af075266a 100755 --- a/packages/docs/build-docs.sh +++ b/packages/docs/build-docs.sh @@ -1,16 +1,6 @@ #!/bin/bash set -euo pipefail -PYTHON_DEPS="$PWD/python-deps" -mkdir -p "${PYTHON_DEPS}" - -export PYTHONPATH=${PYTHON_DEPS}/lib/python3.6/site-packages -export PATH=${PYTHON_DEPS}/bin:$PATH - -#---------------------------------------------------------------------- -# Install python depednencies to a local tree -pip install --ignore-installed --install-option="--prefix=${PYTHON_DEPS}" -r requirements.txt - #---------------------------------------------------------------------- # CONFIG staging="dist/staging" diff --git a/packages/docs/package.json b/packages/docs/package.json index 21358f86dd262..84e9285b9312c 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -2,16 +2,13 @@ "name": "docs", "private": true, "version": "0.6.0", - "description": "AWS CDK Documentation", + "description": "Generates the documentation for all packages", "main": "lib/index.js", "types": "lib/index.d.ts", "repository": { "type": "git", "url": "git://github.com/awslabs/aws-cdk" }, - "pkglint": { - "ignore": true - }, "scripts": { "prepare": "/bin/bash ./build-docs.sh" }, @@ -20,12 +17,11 @@ "url": "https://aws.amazon.com" }, "license": "Apache-2.0", - "devDependencies": { - "aws-cdk-all": "^0.6.0", - "pkgtools": "^0.6.0" + "dependencies": { }, - "keywords": [ - "aws", - "cdk" - ] + "devDependencies": { + "jsii-pacmak": "^0.4.0", + "pkgtools": "^0.6.0", + "aws-cdk-all": "^0.6.0" + } } diff --git a/packages/docs/src/cloudformation.rst b/packages/docs/src/cloudformation.rst index cf8a249c0b391..2c705a4ee250a 100644 --- a/packages/docs/src/cloudformation.rst +++ b/packages/docs/src/cloudformation.rst @@ -1,240 +1,163 @@ -.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 - International License (the "License"). You may not use this file except in compliance with the - License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. - - This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. See the License for the specific language governing permissions and - limitations under the License. - -.. _cloudformation: - -######################### -Using |level1| Constructs -######################### - -|level1| constructs are low-level constructs that provide a direct, one-to-one, -mapping to an |cfn| resource, -as listed in the |cfn| topic `AWS Resource Types Reference `_. - -Avoid using |level1| constructs if possible. -Instead, use |level2| constructs where you don't really need to -know anything about |cfn|. You just define your intent and the underlying resources are -defined for you. - -However, there are use cases where you need to directly define |cfn| resources, -such as when migrating from an existing template. -In those case you can use |level1| constructs -to define |cfn| entities such as resources, parameters, outputs, and conditions. - -If you decide to create an |l2|, -especially one that you want to contribute to the |cdk| package, -see :doc:`creating-constructs`. - -.. _aws_constructs_versus_cfn_resources: - -|level2| Constructs versus CloudFormation Resources -=================================================== - -The |cdk| includes a set of libraries with constructs for defining AWS -resources that we call |level2| constructs. -For example, the :py:mod:`aws-cdk-sns` library includes the `Topic` -construct that you can use to define an |SNS| topic: - -.. code-block:: js - - import { Topic } from 'aws-cdk-sns'; - const topic = new Topic(this, 'MyTopic'); - -|level2| constructs encapsulate the -details of working with these AWS resources. For example, to subscribe a queue to a topic, -call the :py:meth:`aws-cdk-sns.Topic.subscribeQueue` method with a queue object as the second argument: - -.. code-block:: js - - const topic = new Topic(this, 'MyTopic'); - const queue = new Queue(this, 'MyQueue', { - visibilityTimeoutSec: 300 - }); - - topic.subscribeQueue('TopicToQueue', queue); - -This method: - -1. Creates a subscription and associates it with the topic and the queue. - -2. Adds a queue policy with permissions for the topic to send messages to the queue. - -The |CDK| also includes a low-level library (:py:mod:`aws-cdk-resources`) which -includes constructs for all |CFN| resources. This library is automatically -generated based on the `CloudFormation resource specification -`_ -and exposes the declarative API of CloudFormation resources. - -To achieve a similar result using :py:mod:`aws-cdk-resources`, you have to explicitly define the -subscription and queue policy, since there is no **subscribeToQueue** method in the **TopicResource** class: - -.. code-block:: js - - const topic = new sns.TopicResource(this, 'MyTopic'); - const queue = new sqs.QueueResource(this, 'MyQueue'); - - new sns.SubscriptionResource(this, 'TopicToQueue', { - topicArn: topic.ref, // ref == arn for topics - endpoint: queue.queueName, - protocol: 'sqs' - }); - - const policyDocument = new PolicyDocument(); - policyDocument.addStatement(new PolicyStatement() - .addResource(queue.queueArn) - .addAction('sqs:SendMessage') - .addServicePrincipal('sns.amazonaws.com') - .setCondition('ArnEquals', { 'aws:SourceArn': topic.ref })); - - new sqs.QueuePolicyResource(this, 'MyQueuePolicy', { - policyDocument: policyDocument, - queues: [ queue.ref ] - }); - -This example shows one of the many benefits -of using the |level2| constructs instead of the |level1| constructs. - -.. _construct_attributes: - -Construct Attributes -==================== - -To reference the runtime attributes of constructs, -use one of the properties available on the construct object. - -The following example configures a |LAM| function's dead letter queue to use a -the ARN of an |SQS| queue resource. - -.. code-block:: js - - import { lambda, sqs } from 'aws-cdk-resources' - - const dlq = new sqs.QueueResource(this, { name: 'DLQ' }); - - new lambda.FunctionResource(this, { - deadLetterConfig: { - targetArn: dlq.queueArn - } - }); - -The :py:attr:`aws-cdk.Resource.ref` attribute represents the |cfn| -resource's intrinsic reference (or "Return Value"). For example, for `queue.ref` -will also `refer `_ -to the queue's ARN. If possible, it is preferrable to use an explicitly -named attribute instead of *ref*. - -.. _resource_options: - -Resource Options -================ - -The :py:attr:`aws-cdk.Resource.options` object includes |CFN| options, such as -:code:`condition`, :code:`updatePolicy`, :code:`createPolicy` and -:code:`metadata`, for a resource. - -.. _parameters: - -Parameters -========== - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import { Parameter } from 'aws-cdk'; - - const p = new Parameter(this, 'MyParam', { type: 'String' }); - new sns.TopicResource(this, 'MyTopic', { displayName: p.ref }); - -.. _outputs: - -Outputs -======= - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import { Output } from 'aws-cdk'; - - const queue = new sqs.QueueResource(this, 'MyQueue'); - const out = new Output(this, 'MyQueueArn', { value: queue.queueArn }); - - const import = out.makeImportValue(); - assert(import === { "Fn::ImportValue": out.exportName } - -.. _conditions: - -Conditions -========== - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import { Condition } from 'aws-cdk'; - const cond = new Condition(this, 'MyCondition', { - expression: new FnIf(...) - }); - - const queue = new sqs.QueueResource(this, 'MyQueue'); - queue.options.condition = cond; - -.. _intrinsic_functions: - -Intrinsic Functions -=================== - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import { FnJoin } from 'aws-cdk'; - new FnJoin(",", ...) - -.. _pseudo_parameters: - -Pseudo Parameters -================= - -.. NEEDS SOME INTRO TEXT - -.. code-block:: js - - import { AwsRegion } from 'aws-cdk'; - new AwsRegion() - -.. _multiple_environments: - -Creating Multiple Environments -============================== - -As described in :doc:`concepts`, -an environment is the combination of account and Region. -To deploy |cdk| constructs to multiple Regions, you need multiple environments. -The simplest way to manage this action is to define your environments in the -|cx-json| file, as shown in the following example, -which defines a development environment in **us-west-2** -and a production environment in **us-east-1**. - -.. SWAG coming: - -.. code:: json - - "environments": { - "production": { - "account": "123456789012", - "region": "us-east-1" - }, - "dev": { - "account": "123456789012", - "region": "us-west-2" - } - } +.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 + International License (the "License"). You may not use this file except in compliance with the + License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. + + This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific language governing permissions and + limitations under the License. + +.. _cloudformation: + +################### +Using |level1| Constructs +################### + +|level1| constructs are all constructs found in the :py:mod:`aws-cdk-resources` +package. They map directly onto |cfn| resources. + +.. important:: + + In general, you shouldn't need to use this type of Constructs, unless you have + special requirements or there is no |level2| package for the AWS resource you + need yet. Prefer using other packages with higher-level constructs instead. See + :ref:`l1_vs_l2` for a comparison between the construct types. + +.. _construct_attributes: + +Construct Attributes +==================== + +To reference the runtime attributes of constructs, +use one of the properties available on the construct object. + +The following example configures a |LAM| function's dead letter queue to use a +the ARN of an |SQS| queue resource. + +.. code-block:: js + + import { lambda, sqs } from 'aws-cdk-resources' + + const dlq = new sqs.QueueResource(this, { name: 'DLQ' }); + + new lambda.FunctionResource(this, { + deadLetterConfig: { + targetArn: dlq.queueArn + } + }); + +The :py:attr:`aws-cdk.Resource.ref` attribute represents the |cfn| +resource's intrinsic reference (or "Return Value"). For example, for `queue.ref` +will also `refer `_ +to the queue's ARN. If possible, it is preferrable to use an explicitly +named attribute instead of *ref*. + +.. _resource_options: + +Resource Options +================ + +The :py:attr:`aws-cdk.Resource.options` object includes |CFN| options, such as +:code:`condition`, :code:`updatePolicy`, :code:`createPolicy` and +:code:`metadata`, for a resource. + +.. _parameters: + +Parameters +========== + +.. NEEDS SOME INTRO TEXT + +.. code-block:: js + + import { Parameter } from 'aws-cdk'; + + const p = new Parameter(this, 'MyParam', { type: 'String' }); + new sns.TopicResource(this, 'MyTopic', { displayName: p.ref }); + +.. _outputs: + +Outputs +======= + +.. NEEDS SOME INTRO TEXT + +.. code-block:: js + + import { Output } from 'aws-cdk'; + + const queue = new sqs.QueueResource(this, 'MyQueue'); + const out = new Output(this, 'MyQueueArn', { value: queue.queueArn }); + + const import = out.makeImportValue(); + assert(import === { "Fn::ImportValue": out.exportName } + +.. _conditions: + +Conditions +========== + +.. NEEDS SOME INTRO TEXT + +.. code-block:: js + + import { Condition } from 'aws-cdk'; + const cond = new Condition(this, 'MyCondition', { + expression: new FnIf(...) + }); + + const queue = new sqs.QueueResource(this, 'MyQueue'); + queue.options.condition = cond; + +.. _intrinsic_functions: + +Intrinsic Functions +=================== + +.. NEEDS SOME INTRO TEXT + +.. code-block:: js + + import { FnJoin } from 'aws-cdk'; + new FnJoin(",", ...) + +.. _pseudo_parameters: + +Pseudo Parameters +================= + +.. NEEDS SOME INTRO TEXT + +.. code-block:: js + + import { AwsRegion } from 'aws-cdk'; + new AwsRegion() + +.. _multiple_environments: + +Creating Multiple Environments +============================== + +As described in :doc:`concepts`, +an environment is the combination of account and Region. +To deploy |cdk| constructs to multiple Regions, you need multiple environments. +The simplest way to manage this action is to define your environments in the +|cx-json| file, as shown in the following example, +which defines a development environment in **us-west-2** +and a production environment in **us-east-1**. + +.. SWAG coming: + +.. code:: json + + "environments": { + "production": { + "account": "123456789012", + "region": "us-east-1" + }, + "dev": { + "account": "123456789012", + "region": "us-west-2" + } + } diff --git a/packages/docs/src/concepts.rst b/packages/docs/src/concepts.rst index 8895dec7c29c6..ad086554ce3a3 100644 --- a/packages/docs/src/concepts.rst +++ b/packages/docs/src/concepts.rst @@ -50,26 +50,32 @@ to instantiate the ``StorageLayer`` construct. When you initialize a construct, add the construct to the construct tree by specifying the parent construct as the first initializer parameter, -a unique (to your stack) identifier for the construct as the second parameter, -and an optional set of properties for the final parameter, +a unique identifier for the construct as the second parameter, +and an set of properties for the final parameter, as shown in the following example. .. code-block:: js - new MyConstruct(parent, name[, props]); + new SomeConstruct(parent, name[, props]); -From this point on, use the keyword |this| instead of |parent|, -because in most cases the |cdk| initializes new -constructs from within the context of their parent construct, -so |this| represents the parent. +In almost call cases, you will want to pass the keyword ``this`` for the ``parent`` +argument, because you will generally initialize new constructs in the context of +the parent construct. Any descriptive string will do for the ``name`` +argument, +and an in-line object for the set of properties. .. code-block:: js - new BeautifulConstruct(this, 'Foo', ...)) + new BeautifulConstruct(this, 'Foo', { + applicationName: 'myApp', + timeout: 300 + }); + +.. admonition:: Rationale -The reason we associate the construct to its parent as part of its -initialization is because the construct occasionally needs contextual -information from its parent, such as to which the Region the stack is deployed. + The reason we associate the construct to its parent as part of its + initialization is because the construct occasionally needs contextual + information from its parent, such as to which the Region the stack is deployed. Use the following operations to inspect the construct tree. @@ -82,7 +88,7 @@ Use the following operations to inspect the construct tree. :py:meth:`aws-cdk.Construct.getChild` returns the child construct with the specified ID. -:py:meth:`aws-cdk.Construct.toTreeString` + returns a string representing the construct's tree. .. _construct_names: @@ -90,15 +96,11 @@ Use the following operations to inspect the construct tree. Construct Names --------------- -Every construct in a CDK app must have a **name** unique amongst it's siblings. Names -are used to allocate stack-wide unique logical IDs for each CloudFormation -resource. -This value has nothing to do with any similar name attribute in the actual resource. -For example, you could instantiate a construct that creates a table where you store your music files, -hence you create the database table "music", -and name your construct "LambdaMusicTable". +Every construct in a CDK app must have a **name** unique amongst its siblings. +Names are used to track Constructs in the Construct hierarchy, and to allocate +logical IDs so that CloudFormation can keep track of the generated resources. -When a construct is created, it's name is specified as the second +When a construct is created, its name is specified as the second initializer argument: .. code-block:: js @@ -111,38 +113,49 @@ initializer argument: Use the :py:attr:`aws-cdk.Construct.path` property to get the path of this construct from the root of the tree. +Note that the name of a construct does not directly map onto the physical name +of the resource when it is created! If you have a bucket or table that you want +to give a concrete name, then specify the desired name using use the appropriate +property, such as ``bucketName`` or ``tableName``. Example: + +.. code-block:: js + + new Bucket(this, 'MyBucket', { + bucketName: 'physical-bucket-name' + }); + +In general however, you should avoid specifying physical names. Instead, let +CloudFormation generate names for you, and use attributes like bucket.bucketName +to discover the generated names and pass them to your application's runtime +code, as described in :ref:`creating_runtime_value`. + When you synthesize an |cdk| tree into a |CFN| template, the |CFN| logical ID for each resource in the template is allocated according to the path of that -resource in the construct tree. The IDs must be stable when you modify your -stack, so that |CFN| can associate a deployed resource with a template resource -across updates. This is important to know when you are refactoring your code - -make sure to keep construct names stable. -If you have to change the name, see -*Changing Logical IDs*. +resource in the construct tree. For more information, see :ref:`logical_ids`. .. _construct_properties: Construct Properties -------------------- -All constructs accept an optional property class through which you can set the construct's public properties. -Since these classes are defined as TypeScript interfaces, you can pass a property object to your construct -in two ways: +Constructs can be customized by passing a property object as the third +parameter. Every construct has its own set of parameters, defined as an +interface. You can pass a property object to your construct in two ways: .. code-block:: js + // Inline (recommended) + new Queue(this, 'MyQueue', { + visibilityTimeout: 300 + }); + + // Instantiate separate property object const props: QueueProps = { visibilityTimeout: 300 }; new Queue(this, 'MyQueue', props); - // OR the recommended way: - - new Queue(this, 'MyQueue', { - visibilityTimeout: 300 - }); - .. _construct_metadata: Construct Metadata @@ -154,32 +167,6 @@ automatically include the stack trace from which the metadata entry was added, so at any level of a construct you can find the code location, even if metadata was created by a lower-level library that you don't own. -.. _construct_validation: - -Validating Constructs ---------------------- - -Overload the :py:meth:`aws-cdk.Construct.validate` method in your construct. -This method should return either a list of validation error messages, -or an empty list to indicate there are no validation errors. -Construct implementors can then use this method to perform custom -validation to avoid synthesizing or deploying invalid stacks. - -For example: - -.. code-block:: js - - class MyConstruct extends Construct { - validate() { - if (this.getChildren().length > 1) { - return [ 'this construct can only have a single child' ]; - } - else { - return [ ]; - } - } - } - .. _stacks: Stacks @@ -234,11 +221,13 @@ the |cdk| assigns a which must be unique within the template, to each resource in the stack. -When you update the template, |CFN| uses these logical IDs to plan the update -and apply changes. Therefore, logical IDs must remain "stable" across updates. -If you make a modification in your code that results in a change to a logical ID -of a resource, |CFN| deletes the resource and recreates a new resource when it -updates the stack. +.. important:: + + When you update the template, |CFN| uses these logical IDs to plan the update + and apply changes. Therefore, logical IDs must remain "stable" across updates. + If you make a modification in your code that results in a change to a logical ID + of a resource, |CFN| deletes the resource and recreates a new resource when it + updates the stack. Each resource in the construct tree has a unique path that represents its location within the tree. The logical ID of a resource is formed by @@ -276,8 +265,7 @@ Logical IDs are unique within the stack Logical IDs remain unchanged across updates This is true as long as their location within the construct tree doesn't change. - See - *Changing Logical IDs* + See :ref:`creating_runtime_value` for information on how to retain logical IDs despite structural changes in your stack. @@ -332,14 +320,13 @@ stacks. `cdk diff` will tell you which resources are about to be destroyed: .. _environments: -Environments -============ +Environments and authentication +=============================== The |cdk| refers to the combination of an account ID and a Region as an *environment*. The simplest environment is the one you get by default, which is the one you get when you have set up your credentials and a default Region as described in -the *Specifying your Credentials and Region* section of the -:doc:`getting-started` topic. +:ref:`credentials_and_region`. When you create a |stack-class| instance, you can supply the target deployment environment for the stack using the **env** property, as shown in the following example, @@ -349,32 +336,47 @@ where REGION is the Region in which you want to create the stack and ACCOUNT is new MyStack(app, { env: { region: 'REGION', account: 'ACCOUNT' } }); -If you do not supply an account or Region for a stack, the |cdk| -attempts to determine them in the following order. +For each of the two arguments **region** and **account**, the |cdk| uses the +following lookup procedure: -#. It will attempt to read **default-account** and **default-region** from the application's context. +#. If **region** or **account** are provided directly as an property to the + Stack, use that. +#. Otherwise, read **default-account** and **default-region** from the application's context. These can be set in the |toolkit| in either the local |cx-json| file or the global version in *$HOME/.cdk* on Linux or MacOS or *%USERPROFILE%\\.cdk* on Windows. -#. If these are not defined, it will attempt to determine the account based on the - access key ID and secret access key in *$HOME/.aws/credentials* on Linux or MacOS - or *%USERPROFILE%\\.aws\\credentials* on Windows, - and the Region based on the Region in - *$HOME/.aws/config* on Linux or MacOS or *%USERPROFILE%\\.aws\\config* on Windows. - You can set these values manually, but we recommend you use **aws configure**, - as described in the :doc:`getting-started` topic. -#. Finally, if the account has not yet been identified, - it will call **STS.getCallerIdentity** to get the account information. +#. If these are not defined, it will determine them as follows: + * **account**: use account from default SDK credentials. Environment + variables are tried first (**AWS_ACCESS_KEY_ID** and **AWS_SECRET_ACCESS_KEY**), + followed by credentials in *$HOME/.aws/credentials* on Linux or MacOS + or *%USERPROFILE%\\.aws\\credentials* on Windows. + * **region**: use the default region configured in *$HOME/.aws/config* on + Linux or MacOS or *%USERPROFILE%\\.aws\\config* on Windows. + * You can set these defaults manually, but we recommend you use ``aws + configure``, as described in the :doc:`getting-started` topic. We recommend you use the default environment for development stacks, and explicitly specify accounts and Regions for production stacks. +.. note:: + + Note that even though the region and account might explicitly be set on your + Stack, if you run ``cdk deploy`` the |cdk| will still use the + currently-configured SDK credentials, as provided via the **AWS_** + environment variables or ``aws configure``. This means that if you want to + deploy stacks to multiple accounts, you will have to set the correct + credentials for each invocation to ``cdk deploy STACK``. + + In the future, we will provide the ability to specify credential sources for + individual accounts so that you can deploy to multiple accounts using one + invocation of ``cdk deploy``, but this feature is not available yet. + .. _environment_context: -Environment Context -------------------- +Environmental Context +--------------------- When you synthesize a stack to create a |CFN| template, the |cdk| may need information based on the -environment (account and Region), such as the AMIs available in the Region. +environment (account and Region), such as the availability zones or AMIs available in the Region. To enable this feature, the |toolkit| uses *context providers*, and saves the context information into |cx-json| the first time you call |cx-synth-code|. @@ -387,7 +389,7 @@ The |cdk| currently supports the following context providers. .. code:: js - const zones: string[] = new AvailabilityZoneProvider(this).getAZs(); + const zones: string[] = new AvailabilityZoneProvider(this).availabilityZones; for (let zone of zones) { // do somethning for each zone! diff --git a/packages/docs/src/conf.py b/packages/docs/src/conf.py index acfe4df9b4f88..2e0a395d1fe0a 100644 --- a/packages/docs/src/conf.py +++ b/packages/docs/src/conf.py @@ -22,6 +22,10 @@ # # AWS Sphinx configuration file. # +# For more information about how to configure this file, see: +# +# https://w.amazon.com/index.php/AWSDevDocs/Sphinx +# # # General information about the project. diff --git a/packages/docs/src/creating-constructs.rst b/packages/docs/src/creating-constructs.rst index 0784c3d72a446..cd9d3d17bd23a 100644 --- a/packages/docs/src/creating-constructs.rst +++ b/packages/docs/src/creating-constructs.rst @@ -105,3 +105,33 @@ Finally for the package: select your branch, and next to the **Branch** menu select **New pull request**. See the **aws-cdk-dynamodb** package for examples. + +Validation +---------- + +Validation happens in one of two places: + +* In the constructor, to validate the properties that are passed in. +* If the Construct offers methods that mutate the state of the Construct, + in the Construct's :py:meth:`aws-cdk.Construct.validate` method. This + method is called by the framework after the Construct hierarchy has been set up, + and is expected to return a list of validation error messages. + +Construct implementors should prefer throwing validation errors in the constructor, +falling back to overriding the :py:meth:`aws-cdk.Construct.validate` method +only if the Construct offers mutating members. + +Example of implementing validate: + +.. code-block:: js + + class MyConstruct extends Construct { + public validate() { + if (this.getChildren().length > 1) { + return [ 'this construct can only have a single child' ]; + } + else { + return [ ]; + } + } + } diff --git a/packages/docs/src/examples.rst b/packages/docs/src/examples.rst index 9a9ab7041eeef..5e59f30ad2954 100644 --- a/packages/docs/src/examples.rst +++ b/packages/docs/src/examples.rst @@ -113,7 +113,7 @@ understanding the |cdk|. .. _dynamodb_example: -Creating a |dynamodb| Table +Creating a |ddb| Table =========================== The following example creates a diff --git a/packages/docs/src/guidelines.rst b/packages/docs/src/guidelines.rst index 834c0770c191b..e3ce83952af27 100644 --- a/packages/docs/src/guidelines.rst +++ b/packages/docs/src/guidelines.rst @@ -14,7 +14,8 @@ Guidelines ########## -This topic describes some guidelines for using the |cdk|. +This topic describes helpful tips for using the |cdk|. These apply when you are +writing Constructs. .. _general_guidelines: @@ -30,11 +31,13 @@ but do not rename the construct as described in the |cfn| deletes the old construct, including any resources created by the construct, when you deploy the new construct. -* Use TypeScript unless you are a single-language shop +**Use TypeScript unless you are a single-language shop.** + You can use constructs made in TypeScript in any other supported language. The converse is not supported. -* Use compositions instead of inheritance +**Use compositions instead of inheritance.** + Most constructs should directly extend the `Construct` class instead of another construct. For example, @@ -49,24 +52,30 @@ Property Guidelines These are the recommended guidelines for construct properties. -* Use strongly-typed properties +**Use strongly-typed properties.** + This includes using enums instead of primitive types such as strings or ints. -* Do not use tokens for property types +**Do not use tokens for property types.** + Since tokens are a functional interface, they provide no type information and may not translate into a type-safe member during translation to another programming language. -* Provide reasonable default values wherever possible +**Provide reasonable default values wherever possible.** + Most properties should be optional so the user can `new` the construct and get a reasonable object. -* Validate properties during initialization +**Validate properties during initialization.** + Fail early -* Avoid renaming existing AWS resource property names +**Avoid renaming existing AWS resource property names.** + It adds another level of cognitive dissonance to debugging -* Indicate units in primitive property type names +**Indicate units in primitive property type names.** + For example, if the property is an int and specifies how long to wait in seconds, the name could be **waitSec**. @@ -77,21 +86,25 @@ Library Guidelines Use these guidelines when constructing a library. -* Code should be under *lib/*. +Code should be under *lib/*. -* The entry point should be *lib/index.ts* and should only contain +The entry point should be *lib/index.ts* and should only contain **import** statements (importing other TypeScript files in the same directory) -* There is no need to put every class in a separate file. Think of the user. +**There is no need to put every class in a separate file. Think of the user.** + +**Keep your unit test utility code separate from your constructs.** -* Keep your unit test utility code separate from your constructs If you want to make them to be "package-private", put them in a separate file and import them (`import ../lib/my-util`) in your tests -* Keep your integration tests in *test/* as *integ-xxx.ts* +**Keep your integration tests in *test/* as *integ-xxx.ts*.** + They should be |cdk| apps that can be deployed with |cx-deploy-bold|. -* Always create a *README.md* file that includes: +**Always create a *README.md* file.** + + It should include: - The maturity level - An example for each common use case, diff --git a/packages/docs/src/index.rst b/packages/docs/src/index.rst index 870b553ab66ed..640c7d2399ef2 100644 --- a/packages/docs/src/index.rst +++ b/packages/docs/src/index.rst @@ -18,8 +18,9 @@ AWS Cloud Development Kit User Guide welcome getting-started concepts + l1-vs-l2 cloudformation - using-constructs + writing-cdk-apps creating-constructs examples tools diff --git a/packages/docs/src/l1-vs-l2.rst b/packages/docs/src/l1-vs-l2.rst new file mode 100644 index 0000000000000..98bb8059a4393 --- /dev/null +++ b/packages/docs/src/l1-vs-l2.rst @@ -0,0 +1,117 @@ +.. Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 + International License (the "License"). You may not use this file except in compliance with the + License. A copy of the License is located at http://creativecommons.org/licenses/by-nc-sa/4.0/. + + This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + either express or implied. See the License for the specific language governing permissions and + limitations under the License. + +.. _l1_vs_l2: + +##################### +The Construct Library +##################### + +The |cdk| standard library comes with two different types of Constructs: + +|level1| Constructs + + |level1| constructs are low-level constructs that provide a direct, one-to-one, + mapping to an |cfn| resource, + as listed in the |cfn| topic `AWS Resource Types Reference `_. + + All |level1| constructs are found in the :py:mod:`aws-cdk-resources` package. + +|level2| Constructs + + These constructs have been handwritten by AWS engineers and come with + convenient defaults and additional knowledge about the inner workings of the + AWS resources they represent. In general, you will be able to express your + intent without worrying about the details too much, and the correct resources + will automatically be defined for you. + + |level2| constructs are those that are found in all of the other **aws-cdk-** + packages. See the :ref:`reference` section for a list of all available |level2| packages + and constructs. + +Prefer using |level2| constructs whenever possible, as they will provide the +best developer experience. + +Sometimes there are use cases where you need to directly define |cfn| resources, +such as when migrating from an existing template, or when there is no |level2| +library for the AWS resources you need yet. +In those case you can use |level1| constructs +to define |cfn| entities such as Resources, Parameters, Outputs, and Conditions. + +Of course, it's always possible to define your own higher-level constructs in +addition to the ones already provided, simply by defining a new class that +describes your construct. For more information, see :doc:`creating-constructs`. + +.. _aws_constructs_versus_cfn_resources: + +|level2| Constructs versus |level1| Constructs +============================================== + +To illustrate the advantages that |level2| constructs have +|level1| constructs, let's look at an example. + +The :py:mod:`aws-cdk-sns` |level2| includes the `Topic` construct that you can +use to define an |SNS| topic: + +.. code-block:: js + + import { Topic } from 'aws-cdk-sns'; + const topic = new Topic(this, 'MyTopic'); + +|level2| constructs encapsulate the +details of working with these AWS resources. For example, to subscribe a queue to a topic, +call the :py:meth:`aws-cdk-sns.Topic.subscribeQueue` method with a queue object as the second argument: + +.. code-block:: js + + const topic = new Topic(this, 'MyTopic'); + const queue = new Queue(this, 'MyQueue', { + visibilityTimeoutSec: 300 + }); + + topic.subscribeQueue('TopicToQueue', queue); + +This method: + +1. Creates a subscription and associates it with the topic and the queue. + +2. Adds a queue policy with permissions for the topic to send messages to the queue. + +To achieve a similar result using :py:mod:`aws-cdk-resources`, you have to explicitly define the +subscription and queue policy, since there is no **subscribeToQueue** method in the **TopicResource** class: + +.. code-block:: js + + const topic = new sns.TopicResource(this, 'MyTopic'); + const queue = new sqs.QueueResource(this, 'MyQueue'); + + new sns.SubscriptionResource(this, 'TopicToQueue', { + topicArn: topic.ref, // ref == arn for topics + endpoint: queue.queueName, + protocol: 'sqs' + }); + + const policyDocument = new PolicyDocument(); + policyDocument.addStatement(new PolicyStatement() + .addResource(queue.queueArn) + .addAction('sqs:SendMessage') + .addServicePrincipal('sns.amazonaws.com') + .setCondition('ArnEquals', { 'aws:SourceArn': topic.ref })); + + new sqs.QueuePolicyResource(this, 'MyQueuePolicy', { + policyDocument: policyDocument, + queues: [ queue.ref ] + }); + +Notice how much cleaner the first version is. There is more focus on intent, +rather than mechanism. + +This example shows one of the many benefits +of using the |level2| constructs instead of the |level1| constructs. diff --git a/packages/docs/src/tools.rst b/packages/docs/src/tools.rst index fa8c1a9b924c8..32510bb38b3ad 100644 --- a/packages/docs/src/tools.rst +++ b/packages/docs/src/tools.rst @@ -15,9 +15,31 @@ Tools ##### cdk -==== +=== + +``cdk`` (the |toolkit|) is the main tool you use to exercise your *CDK App*. It will execute +the CDK app you have written and compiled, interrogate the application +model you have defined, and produce and deploy the CloudFormation templates +generated by it. -Here is the output from **cdk --help**: +You have to tell ``cdk`` what command to use to run your CDK app using the +``--app`` option. For example: + +.. code-block:: sh + + cdk --app 'node bin/main.js' synth + +Because this is rather tedious to repeat every time, you will generally put +this option in a file called *cdk.json*: + +.. code-block:: javascript + + { + "app": "node bin/main.js" + } + + +Below are the actions you can take on your CDK app: .. code-block:: sh @@ -38,7 +60,7 @@ Here is the output from **cdk --help**: stack or a local template file metadata [STACK] Returns all metadata associated with this stack init [TEMPLATE] Create a new, empty CDK project from a template - + Options: --help Show help [boolean] --version Show version number [boolean] @@ -51,9 +73,9 @@ Here is the output from **cdk --help**: --strict Do not construct stacks with warnings [boolean] --json, -j Use JSON output instead of YAML [boolean] --verbose, -v Show debug logs [boolean] - + If your app has a single stack, there is no need to specify the stack name - + If one of cdk.json or ~/.cdk.json exists, options specified there will be used as defaults. Settings in cdk.json take precedence. diff --git a/packages/docs/src/using-constructs.rst b/packages/docs/src/writing-cdk-apps.rst similarity index 58% rename from packages/docs/src/using-constructs.rst rename to packages/docs/src/writing-cdk-apps.rst index 27248658d9af8..49bfb5300e02f 100644 --- a/packages/docs/src/using-constructs.rst +++ b/packages/docs/src/writing-cdk-apps.rst @@ -10,23 +10,20 @@ .. _using_l2_constructs: -###################################### -Using |level2| Constructs in the |cdk| -###################################### +################# +Writing CDK Apps +################# -This topic provides information about using |level2| constructs in the |cdk|. +*CDK Apps* are the applications you write using the CDK libraries. After writing +and compiling them, you'll use the |toolkit| to synthesize or deploy the +stacks they describe. -An |l1| represents a single AWS service resource, -such as an |s3| bucket, |sns| topic, or |sqs| queue; -an |l2| represents two or more |l1| objects; -and a |l3| represents two or more |l2| objects. -You can create your own constructs, as described in -:doc:`cloudformation`, -or use constructs created by others. +To write your CDK app, you can create your own constructs, as described in +:doc:`cloudformation`, or use constructs created by others. .. _incorporating_external_constructs: -Incorporating External Constructs +Incorporating external constructs ================================= To incorporate an external construct, use the **import** statement, @@ -43,10 +40,52 @@ You can then instantiate an instance of **MyGroovyStack**. new MyGroovyStack(app, 'Test'); +.. _organizing_your_app: + +Organizing your application +=========================== + +The natural way to write CDK apps is by defining new constructs. The difference +between your constructs and the Construct Library constructs is that the ones +you will be writing are exactly tailored to your application. + +Defining a construct is as simple as declaring a class that inherits from +**Construct**, and doing whatever work is appropriate in the constructor. + +The simplest custom Constructs you will be writing are **Stacks**. They define +the individually deployable components of your application. + +The main entry point of your CDK App will instantiate an instance of **App**, +add instantiate each of your stacks **Stacks** as a child of the **App** (or +potentially, instantiate the same **Stack** multiple times but with different +arguments, for example if you want to deploy the stame stack to different +regions). + +See the :ref:`cdk_examples` section for a number of examples on writing a simple +stack-plus-app combination. For real code, we recommend separating the +entry point and the stack's class into different files, though. + +Evolving your application's constructs +====================================== + +Once your stack grows too big, it may make sense to define individual constructs +for logical pieces that make sense together. For example, if your application +contains a queue and some compute to work on that queue, it might make sense to +define a new construct called **QueueProcessor** that codifies that pattern. If +the new construct is successful, you might even consider adding some parameters +to it to make it more reusable, and sharing it among your projects, or even +sharing it with other people. + +Writing a CDK app means breaking down your desired infrastructure into logical +constructs, and reusing them wherever it makes sense. For more information on +writing constructs, see :ref:`creating_constructs`, :ref:`guidelines` and of +course the :ref:`cdk_examples`. + + .. _runtime_discovery: -Incorporating Constructs at Runtime -=================================== +Referencing Resources at Runtime +================================ As you create constructs in the |cdk|, you will likely want to be able to refer to the created resources at runtime.