diff --git a/packages/docs/src/_includes.txt b/packages/docs/src/_includes.txt index c4cf967c5d111..66d57de215e50 100644 --- a/packages/docs/src/_includes.txt +++ b/packages/docs/src/_includes.txt @@ -30,15 +30,6 @@ .. |resource-construct| replace:: *Resource* -.. |level1| replace:: AWS Resource -.. |level2| replace:: AWS Construct Library -.. |level3| replace:: purpose-built - -.. |l1| replace:: |level1| construct -.. |l2| replace:: |level2| construct -.. |l3| replace:: |level3| construct -.. l4 App / Applet ??? - .. |aws-home| replace:: https://aws.amazon.com .. Installation entities diff --git a/packages/docs/src/cloudformation.rst b/packages/docs/src/cloudformation.rst index cf8a249c0b391..6d54adf04b9f4 100644 --- a/packages/docs/src/cloudformation.rst +++ b/packages/docs/src/cloudformation.rst @@ -1,240 +1,135 @@ -.. 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 CloudFormation Resource Constructs +################### + +CloudFormation Resource constructs are 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 Construct Library 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() diff --git a/packages/docs/src/concepts.rst b/packages/docs/src/concepts.rst index 8895dec7c29c6..5364c9e3c0f2f 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 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 all 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/creating-constructs.rst b/packages/docs/src/creating-constructs.rst index 0784c3d72a446..b0fcc6401f312 100644 --- a/packages/docs/src/creating-constructs.rst +++ b/packages/docs/src/creating-constructs.rst @@ -21,41 +21,44 @@ This topic provides information on how to create constructs in the |cdk|. What Are Constructs? ==================== -As described in :doc:`concepts`, -constructs are components that developers can combine into other constructs and -an application stack to create an |cdk| application. +As described in :doc:`concepts`, constructs are components that developers can +combine into other constructs and an application stack to create an |cdk| +application. -The lowest-level |cdk| construct is an |l1|, -which represents a single AWS service resource, -such as an SNS topic or an SQS queue. +The lowest-level |cdk| construct is a Resource Construct, which represents a +single AWS service resource, such as an SNS topic or an SQS queue. -The next level of constructs, -which we call an |l2|, -consist of two or more |level1| constructs. -For example, the stack described in the :doc:`getting-started` topic consists of an |SQS| queue, -|SNS| topic, a subscription between the topic and the queue, -and an |IAM| policy document. +The next level of constructs, which we call a Construct Library Construct, +typicall wraps one or more Resource Constructs and adds higher-level +abstractions. These can include: -The |cdk| includes a variety of |level2| constructs, -and you can create your own for special needs. +* Convenient defaults. +* Automatic creation of related resources such as policy documents, IAM roles or encryption keys. +* Methods to make the resource interact with other resources. -The |cdk| also includes a library of |level3| constructs -that consist of two or more |l2| objects, -where some or most of the run-time attributes, such as the Region or availability zone, are specified. +For example, the stack described in the :doc:`getting-started` topic consists of +an |SQS| queue, |SNS| topic, a subscription between the topic and the queue, and +an |IAM| policy document. -To create an |l1|, see :doc:`cloudformation`. +The |cdk| includes a variety of Library constructs, and you can create your own +for special needs. You might even consider writing and publishing purpose-built +constructs (:ref:`purpose_built_constructs`) to solve common business needs. Be +sure to have a look at the :ref:`guidelines` section on tips for developing +new constructs. -The rest of this topic describes how to create an |l2| -and contribute it to the |cdk| as a pull request. +Whether you're planning to write a purpose-built construct for your own +application or submit a new Construct Library construct to the CDK, the process +is quite similar. It's described below: .. _creating_l2_constructs: -Creating |level2| Constructs -============================ +Creating Construct Library constructs +===================================== -To create a new |l2| as a pull request to the |cdk|: +To create a new Construct Library construct as a pull request to the |cdk|: -* Clone the |cdk| project to your computer: +* Fork the |cdk| project to your own GitHub account. +* Clone the repository to your computer: .. code-block:: sh @@ -78,7 +81,7 @@ To create a new |l2| as a pull request to the |cdk|: where *NAME* indicates the functionality within the file. * Create *NAME.ts* for each set of related classes, - such as an L2 and its props. + such as the construct itself and its props interface. In *NAME.ts*: @@ -97,11 +100,44 @@ In *NAME.ts*: Finally for the package: -* Add a test, *test.NAME.ts*, for each L2 construct. +* Add a test, *test.NAME.ts*, for each construct. * Create an integration test, *integ.everything.ts*. -* Once everything looks good, navigate to the |cdk| project in GitHub, - select your branch, and next to the **Branch** menu select **New pull request**. +* Commit your changes and push them back to GitHub. + +* Once everything looks good, navigate to the |cdk| project on the GitHub + website, 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..3287f3878453c 100644 --- a/packages/docs/src/examples.rst +++ b/packages/docs/src/examples.rst @@ -14,8 +14,8 @@ Examples ######## -This topic contains some |level2| construct examples to help you get started -understanding the |cdk|. +This topic contains some usage examples to help you get started understanding +the |cdk|. .. We'll include this if we ever implement DeploymentPipeline _multiple_stacks_example: @@ -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..acb945fd1e3ba 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,17 +31,18 @@ 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 - Most constructs should directly extend the `Construct` class - instead of another construct. - For example, - when defining a |level2| construct that represents an AWS resource, - do not extend the corresponding |level1| construct. - Instead, extend the `Construct` class and add the |level1| resource as a child. +**Use compositions instead of inheritance.** + + Most constructs should directly extend the **Construct** class instead of + another construct. For example, when defining a Library construct that + represents an AWS resource, do not extend the corresponding Resource + construct. Instead, extend the **Construct** class and add the resource as a + child. Give the primary Resource construct the name ``"Resource"``. .. _property_guidelines: @@ -49,24 +51,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 +85,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..0698b24cf1ee1 --- /dev/null +++ b/packages/docs/src/l1-vs-l2.rst @@ -0,0 +1,135 @@ +.. 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: + +AWS CloudFormation Resource Constructs + + CloudFormation Resource 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 Resource constructs are found in the :py:mod:`aws-cdk-resources` package. + +AWS Construct Library 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. + + Construt Library 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 Construct Library packages and constructs. + +Prefer using Construct Library 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 Construct +Library for the AWS resources you need yet. In those case you can use Resource +constructs to define CloudFormation 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: + +Construct Library constructs vs CloudFormation Resource constructs +================================================================== + +To illustrate the advantages that Construct Library constructs have over +CloudFormation Resource constructs, let's look at an example. + +The :py:mod:`aws-cdk-sns` Construct 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'); + +Library 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 Library constructs instead of the Resource constructs. + +.. _purpose_built_constructs: + +Purpose-built constructs +======================== + +At an even higher-level than Construct Library constructs, *Purpose-built +constructs* are constructs that aggregate multiple other constructs together +into common architectural patterns, such as a *queue processor* or an *HTTP +service*. + +By leveraging these common patterns, you will be able to assemble your +application even faster than by using Construct Library constructs directly. + +Because these constructs will depend on your application's specific goals and +requirements, they are not included with the standard CDK Construct +Library. Instead, we encourage you to develop and share them inside your +organization or on GitHub. 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/welcome.rst b/packages/docs/src/welcome.rst index b88ea7becf9ae..e9723ef09bc08 100644 --- a/packages/docs/src/welcome.rst +++ b/packages/docs/src/welcome.rst @@ -30,7 +30,7 @@ the low-level building blocks. The |cdk| takes a code-first approach to cloud architectures and allows developers to use familiar object-oriented idioms to describe their architecture -**constructs**. +**constructs**. .. note:: There is no charge for using the |cdk|, however you may incur AWS charges for creating or using AWS `chargeable resources `_, @@ -53,7 +53,7 @@ The |cdk| uses the following terms. construct The building block of an |cdk| app or library. In code, they are instances of - the :py:class:`aws-cdk.Construct` class or a class that extends the + the :py:class:`aws-cdk.Construct` class or a class that extends the :py:class:`aws-cdk.Construct` class. app @@ -72,7 +72,7 @@ applet A reusable |cdk| construct that can be instantiated and deployed through a YAML-format file. -|l1| +CloudFormation Resource construct The lowest-level construct, which map directly to an |CFN| resource, as described in the `AWS Resource Types Reference `_. @@ -80,14 +80,16 @@ applet as the |CFN| name with a **Resource** suffix within the AWS service namespace, such as **sqs.QueueResource** representing an |SQS| queue. -|l2| +Construct Library construct A construct that provides high level APIs for a AWS services. Their names imply the underlying AWS service. - For example, |s3| resources are available through the **aws-cdk-s3** |l2|. + For example, |s3| resources are available through the **aws-cdk-s3** + Construct Library. -|l3| - Purpose-built construct, designed to implement a specific task on AWS. For example, - the :py:mod:`???` library provides a construct API ???. +Purpose-built construct + Purpose-built construct, designed to abstract away common architectural + patterns on AWS. These are not supplied with the standard CDK distribution, + but are shared within your organization or on GitHub. .. _aws_cdk_additional_resources: 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.