Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(s3-deployment): graduate to stable 🚀 #13906

Merged
merged 2 commits into from Apr 2, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 32 additions & 7 deletions packages/@aws-cdk/aws-s3-deployment/README.md
Expand Up @@ -3,13 +3,7 @@

---

![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)

> The APIs of higher level constructs in this module are experimental and under active development.
> They are subject to non-backward compatible changes or removal in any future version. These are
> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be
> announced in the release notes. This means that while you may use them, you may need to update
> your source code when upgrading to a newer version of this package.
![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)

---

Expand Down Expand Up @@ -70,6 +64,37 @@ By default, the contents of the destination bucket will **not** be deleted when
changed. You can use the option `retainOnDelete: false` to disable this behavior,
in which case the contents will be deleted.

Configuring this has a few implications you should be aware of:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered dropping this feature entirely due to these, and instead instruct users to use autoDeleteObjects on the destination bucket.

The problem is that autoDeleteObjects doesn't respect prefixes (nor it should), so its going to delete the entire bucket, while retainOnDelete: false only deletes the specified prefix that it was configured with.

Since our default is true, and we haven't seen any users report these issues, it's probably ok to just surface them a bit and leave as is.


- **Logical ID Changes**

Changing the logical ID of the `BucketDeployment` construct, without changing the destination
(for example due to refactoring, or intentional ID change) **will result in the deletion of the objects**.
This is because CloudFormation will first create the new resource, which will have no affect,
followed by a deletion of the old resource, which will cause a deletion of the objects,
since the destination hasn't changed, and `retainOnDelete` is `false`.

- **Destination Changes**

When the destination bucket or prefix is changed, all files in the previous destination will **first** be
deleted and then uploaded to the new destination location. This could have availablity implications
on your users.

### General Recommendations

#### Shared Bucket

If the destination bucket **is not** dedicated to the specific `BucketDeployment` construct (i.e shared by other entities),
we recommend to always configure the `destinationKeyPrefix` property. This will prevent the deployment from
accidentally deleting data that wasn't uploaded by it.

#### Dedicated Bucket

If the destination bucket **is** dedicated, it might be reasonable to skip the prefix configuration,
in which case, we recommend to remove `retainOnDelete: false`, and instead, configure the
[`autoDeleteObjects`](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-s3-readme.html#bucket-deletion)
property on the destination bucket. This will avoid the logical ID problem mentioned above.

## Prune

By default, files in the destination bucket that don't exist in the source will be deleted
Expand Down
106 changes: 100 additions & 6 deletions packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts
Expand Up @@ -13,6 +13,9 @@ import { ISource, SourceConfig } from './source';
// eslint-disable-next-line no-duplicate-imports, import/order
import { Construct as CoreConstruct } from '@aws-cdk/core';

/**
* Properties for `BucketDeployment`.
*/
export interface BucketDeploymentProps {
/**
* The sources from which to deploy the contents of this bucket.
Expand Down Expand Up @@ -45,11 +48,10 @@ export interface BucketDeploymentProps {
* If this is set to "false", the destination files will be deleted when the
* resource is deleted or the destination is updated.
*
* NOTICE: if this is set to "false" and destination bucket/prefix is updated,
* all files in the previous destination will first be deleted and then
* uploaded to the new destination location. This could have availablity
* implications on your users.
* NOTICE: Configuring this to "false" might have operational implications. Please
* visit to the package documentation referred below to make sure you fully understand those implications.
*
* @see https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-s3-deployment#retain-on-delete
* @default true - when resource is deleted/updated, files are retained
*/
readonly retainOnDelete?: boolean;
Expand Down Expand Up @@ -179,6 +181,10 @@ export interface BucketDeploymentProps {
readonly vpcSubnets?: ec2.SubnetSelection;
}

/**
* `BucketDeployment` populates an S3 bucket with the contents of .zip files from
* other S3 buckets or from local disk
*/
export class BucketDeployment extends CoreConstruct {
constructor(scope: Construct, id: string, props: BucketDeploymentProps) {
super(scope, id);
Expand Down Expand Up @@ -285,17 +291,58 @@ function mapSystemMetadata(metadata: BucketDeploymentProps) {
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html#SysMetadata
*/
export class CacheControl {

/**
* Sets 'must-revalidate'.
*/
public static mustRevalidate() { return new CacheControl('must-revalidate'); }

/**
* Sets 'no-cache'.
*/
public static noCache() { return new CacheControl('no-cache'); }

/**
* Sets 'no-transform'.
*/
public static noTransform() { return new CacheControl('no-transform'); }

/**
* Sets 'public'.
*/
public static setPublic() { return new CacheControl('public'); }

/**
* Sets 'private'.
*/
public static setPrivate() { return new CacheControl('private'); }

/**
* Sets 'proxy-revalidate'.
*/
public static proxyRevalidate() { return new CacheControl('proxy-revalidate'); }

/**
* Sets 'max-age=<duration-in-seconds>'.
*/
public static maxAge(t: cdk.Duration) { return new CacheControl(`max-age=${t.toSeconds()}`); }

/**
* Sets 's-maxage=<duration-in-seconds>'.
*/
public static sMaxAge(t: cdk.Duration) { return new CacheControl(`s-maxage=${t.toSeconds()}`); }

/**
* Constructs a custom cache control key from the literal value.
*/
public static fromString(s: string) { return new CacheControl(s); }

private constructor(public readonly value: any) {}
private constructor(
/**
* The raw cache control setting.
*/
public readonly value: any,
) {}
}

/**
Expand All @@ -304,7 +351,15 @@ export class CacheControl {
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html#SysMetadata
*/
export enum ServerSideEncryption {

/**
* 'AES256'
*/
AES_256 = 'AES256',

/**
* 'aws:kms'
*/
AWS_KMS = 'aws:kms'
}

Expand All @@ -313,12 +368,40 @@ export enum ServerSideEncryption {
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html#SysMetadata
*/
export enum StorageClass {

/**
* 'STANDARD'
*/
STANDARD = 'STANDARD',

/**
* 'REDUCED_REDUNDANCY'
*/
REDUCED_REDUNDANCY = 'REDUCED_REDUNDANCY',

/**
* 'STANDARD_IA'
*/
STANDARD_IA = 'STANDARD_IA',

/**
* 'ONEZONE_IA'
*/
ONEZONE_IA = 'ONEZONE_IA',

/**
* 'INTELLIGENT_TIERING'
*/
INTELLIGENT_TIERING = 'INTELLIGENT_TIERING',

/**
* 'GLACIER'
*/
GLACIER = 'GLACIER',

/**
* 'DEEP_ARCHIVE'
*/
DEEP_ARCHIVE = 'DEEP_ARCHIVE'
}

Expand Down Expand Up @@ -347,11 +430,22 @@ export class Expires {
*/
public static after(t: cdk.Duration) { return Expires.atDate(new Date(Date.now() + t.toMilliseconds())); }

/**
* Create an expiration date from a raw date string.
*/
public static fromString(s: string) { return new Expires(s); }

private constructor(public readonly value: any) {}
private constructor(
/**
* The raw expiration date expression.
*/
public readonly value: any,
) {}
}

/**
* Custom user defined metadata.
*/
export interface UserDefinedObjectMetadata {
/**
* Arbitrary metadata key-values
Expand Down
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-s3-deployment/lib/source.ts
Expand Up @@ -6,6 +6,9 @@ import * as s3_assets from '@aws-cdk/aws-s3-assets';
// eslint-disable-next-line no-duplicate-imports, import/order
import { Construct } from '@aws-cdk/core';

/**
* Source information.
*/
export interface SourceConfig {
/**
* The source bucket to deploy from.
Expand Down
33 changes: 2 additions & 31 deletions packages/@aws-cdk/aws-s3-deployment/package.json
Expand Up @@ -111,37 +111,8 @@
"engines": {
"node": ">= 10.13.0 <13 || >=13.7.0"
},
"stability": "experimental",
"maturity": "experimental",
"awslint": {
"exclude": [
"docs-public-apis:@aws-cdk/aws-s3-deployment.Expires.fromString",
"docs-public-apis:@aws-cdk/aws-s3-deployment.BucketDeployment",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.fromString",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.maxAge",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.mustRevalidate",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.noCache",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.noTransform",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.proxyRevalidate",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.setPrivate",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.setPublic",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.sMaxAge",
"docs-public-apis:@aws-cdk/aws-s3-deployment.Expires.value",
"docs-public-apis:@aws-cdk/aws-s3-deployment.CacheControl.value",
"docs-public-apis:@aws-cdk/aws-s3-deployment.BucketDeploymentProps",
"docs-public-apis:@aws-cdk/aws-s3-deployment.SourceConfig",
"docs-public-apis:@aws-cdk/aws-s3-deployment.UserDefinedObjectMetadata",
"docs-public-apis:@aws-cdk/aws-s3-deployment.ServerSideEncryption.AES_256",
"docs-public-apis:@aws-cdk/aws-s3-deployment.ServerSideEncryption.AWS_KMS",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.STANDARD",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.REDUCED_REDUNDANCY",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.STANDARD_IA",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.ONEZONE_IA",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.INTELLIGENT_TIERING",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.GLACIER",
"docs-public-apis:@aws-cdk/aws-s3-deployment.StorageClass.DEEP_ARCHIVE"
]
},
"stability": "stable",
"maturity": "stable",
"awscdkio": {
"announce": false
},
Expand Down