From 56d64ebdb762c12f0fc386e014cb3a7904c238a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Wed, 12 Jun 2019 10:03:57 +0200 Subject: [PATCH 1/5] feat(core): Introduced Duration class This can be used to model durations in a user-friendly way, while allowing coercion into the desired/required time unit at the usage site. There is now a default requirement that properties exposed by the APIs and that have a duration-like name (ending in `duration`, `period`, `timeout` or `ttl`) have a type of `@aws-cdk/cdk.Duration` and do not contain a unit suffix (`Days`, `Millis`, `Seconds`, `Sec`, ...). BREAKING CHANGE: Properties throughout the AWS Construct Libraries that represent lengths of time have been re-typed to be `@aws-cdk/cdk.Duration` instead of `number`, and were renamed to exclude any unit indication. --- .../assets-docker/lib/adopted-repository.ts | 2 +- packages/@aws-cdk/aws-apigateway/lib/stage.ts | 10 +- packages/@aws-cdk/aws-apigateway/package.json | 5 +- .../lib/step-scaling-action.ts | 6 +- .../lib/step-scaling-policy.ts | 10 +- .../lib/target-tracking-scaling-policy.ts | 17 +- .../aws-autoscaling/lib/auto-scaling-group.ts | 53 +---- .../aws-autoscaling/lib/lifecycle-hook.ts | 6 +- .../lib/step-scaling-action.ts | 10 +- .../lib/step-scaling-policy.ts | 12 +- .../lib/target-tracking-scaling-policy.ts | 16 +- .../test/test.auto-scaling-group.ts | 6 +- .../lib/dns-validated-certificate.ts | 2 +- .../test/integ.trivial-lambda-resource.ts | 2 +- .../aws-cloudformation/test/test.resource.ts | 2 +- .../aws-cloudfront/lib/web_distribution.ts | 45 ++-- packages/@aws-cdk/aws-cloudtrail/package.json | 8 +- packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts | 6 +- packages/@aws-cdk/aws-cloudwatch/lib/graph.ts | 2 +- .../@aws-cdk/aws-cloudwatch/lib/metric.ts | 33 ++- .../aws-cloudwatch/test/test.alarm.ts | 4 +- .../@aws-cdk/aws-codebuild/lib/project.ts | 8 +- packages/@aws-cdk/aws-codebuild/package.json | 6 +- .../aws-codebuild/test/test.codebuild.ts | 2 +- .../@aws-cdk/aws-config/lib/managed-rules.ts | 12 +- .../lib/global-table-coordinator.ts | 2 +- .../lib/scalable-table-attribute.ts | 4 +- packages/@aws-cdk/aws-ec2/test/test.vpn.ts | 6 +- packages/@aws-cdk/aws-ecr/lib/lifecycle.ts | 8 +- packages/@aws-cdk/aws-ecr/lib/repository.ts | 10 +- .../@aws-cdk/aws-ecr/test/test.repository.ts | 2 +- .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 13 +- .../aws-ecs/lib/base/scalable-task-count.ts | 16 +- packages/@aws-cdk/aws-ecs/lib/cluster.ts | 10 +- .../aws-ecs/lib/container-definition.ts | 16 +- .../lib/drain-hook/instance-drain-hook.ts | 16 +- .../aws-ecs/lib/log-drivers/aws-log-driver.ts | 6 +- .../aws-ecs/test/ec2/test.ec2-service.ts | 2 +- .../test/fargate/test.fargate-service.ts | 4 +- .../aws-ecs/test/test.aws-log-driver.ts | 4 +- .../aws-ecs/test/test.container-definition.ts | 12 +- .../lib/load-balancer.ts | 14 +- .../test/test.loadbalancer.ts | 6 +- .../lib/alb/application-listener.ts | 22 +- .../lib/alb/application-load-balancer.ts | 6 +- .../lib/alb/application-target-group.ts | 22 +- .../lib/nlb/network-listener.ts | 8 +- .../lib/shared/base-target-group.ts | 24 +- .../test/alb/test.listener.ts | 6 +- .../test/alb/test.load-balancer.ts | 2 +- .../test/nlb/test.listener.ts | 4 +- .../test/stepfunctions/statemachine.test.ts | 2 +- packages/@aws-cdk/aws-iam/lib/role.ts | 16 +- packages/@aws-cdk/aws-iam/test/test.role.ts | 14 +- packages/@aws-cdk/aws-lambda/lib/function.ts | 16 +- .../@aws-cdk/aws-lambda/lib/log-retention.ts | 4 +- .../aws-lambda/test/integ.log-retention.ts | 6 +- .../@aws-cdk/aws-lambda/test/test.lambda.ts | 10 +- .../aws-lambda/test/test.log-retention.ts | 2 +- .../aws-lambda/test/test.singleton-lambda.ts | 2 +- packages/@aws-cdk/aws-logs/lib/log-group.ts | 6 +- packages/@aws-cdk/aws-logs/package.json | 2 +- .../aws-logs/test/example.retention.lit.ts | 4 +- .../@aws-cdk/aws-logs/test/test.loggroup.ts | 6 +- packages/@aws-cdk/aws-rds/lib/cluster.ts | 2 +- packages/@aws-cdk/aws-rds/lib/instance.ts | 18 +- packages/@aws-cdk/aws-rds/lib/props.ts | 4 +- .../@aws-cdk/aws-rds/lib/secret-rotation.ts | 8 +- .../aws-rds/test/integ.instance.lit.ts | 4 +- .../@aws-cdk/aws-rds/test/test.instance.ts | 130 ++++++----- .../@aws-cdk/aws-route53/lib/hosted-zone.ts | 4 +- .../@aws-cdk/aws-route53/lib/record-set.ts | 12 +- .../aws-route53/test/integ.route53.ts | 2 +- .../aws-route53/test/test.record-set.ts | 4 +- .../@aws-cdk/aws-route53/test/test.route53.ts | 2 +- .../lib/bucket-deployment.ts | 2 +- packages/@aws-cdk/aws-s3/lib/bucket.ts | 21 +- packages/@aws-cdk/aws-s3/lib/rule.ts | 12 +- packages/@aws-cdk/aws-s3/test/test.rules.ts | 12 +- .../lib/rotation-schedule.ts | 8 +- .../aws-servicediscovery/lib/service.ts | 12 +- .../integ.service-with-cname-record.lit.ts | 2 +- ....service-with-private-dns-namespace.lit.ts | 2 +- ...g.service-with-public-dns-namespace.lit.ts | 2 +- packages/@aws-cdk/aws-sns/test/test.sns.ts | 4 +- packages/@aws-cdk/aws-sqs/lib/queue.ts | 30 +-- .../@aws-cdk/aws-sqs/lib/validate-props.ts | 9 +- packages/@aws-cdk/aws-sqs/test/test.sqs.ts | 6 +- .../lib/invoke-activity.ts | 5 +- .../lib/send-to-queue.ts | 5 +- .../test/integ.job-poller.ts | 6 +- packages/@aws-cdk/aws-stepfunctions/README.md | 6 +- .../aws-stepfunctions/lib/state-graph.ts | 7 +- .../aws-stepfunctions/lib/state-machine.ts | 6 +- .../aws-stepfunctions/lib/states/state.ts | 4 +- .../aws-stepfunctions/lib/states/task.ts | 10 +- .../aws-stepfunctions/lib/states/wait.ts | 22 +- .../lib/step-functions-task.ts | 3 +- .../@aws-cdk/aws-stepfunctions/lib/types.ts | 7 +- .../@aws-cdk/aws-stepfunctions/package.json | 6 +- .../aws-stepfunctions/test/test.activity.ts | 4 +- .../test/test.states-language.ts | 4 +- packages/@aws-cdk/cdk/lib/cfn-resource.ts | 14 +- packages/@aws-cdk/cdk/lib/duration.ts | 208 ++++++++++++++++++ packages/@aws-cdk/cdk/lib/index.ts | 1 + packages/@aws-cdk/cdk/lib/resource-policy.ts | 4 +- packages/@aws-cdk/cdk/test/test.duration.ts | 91 ++++++++ tools/awslint/bin/awslint.ts | 9 +- tools/awslint/lib/rules/durations.ts | 51 +++++ tools/awslint/lib/rules/index.ts | 15 +- 110 files changed, 871 insertions(+), 549 deletions(-) create mode 100644 packages/@aws-cdk/cdk/lib/duration.ts create mode 100644 packages/@aws-cdk/cdk/test/test.duration.ts create mode 100644 tools/awslint/lib/rules/durations.ts diff --git a/packages/@aws-cdk/assets-docker/lib/adopted-repository.ts b/packages/@aws-cdk/assets-docker/lib/adopted-repository.ts index 397afb6828b0e..9114c52fc4462 100644 --- a/packages/@aws-cdk/assets-docker/lib/adopted-repository.ts +++ b/packages/@aws-cdk/assets-docker/lib/adopted-repository.ts @@ -38,7 +38,7 @@ export class AdoptedRepository extends ecr.RepositoryBase { handler: 'handler.handler', code: lambda.Code.asset(path.join(__dirname, 'adopt-repository')), uuid: 'dbc60def-c595-44bc-aa5c-28c95d68f62c', - timeout: 300 + timeout: cdk.Duration.minutes(5) }); fn.addToRolePolicy(new iam.PolicyStatement() diff --git a/packages/@aws-cdk/aws-apigateway/lib/stage.ts b/packages/@aws-cdk/aws-apigateway/lib/stage.ts index d89961bbdf2d7..6e4000ca756ba 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/stage.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/stage.ts @@ -1,4 +1,4 @@ -import { Construct, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Resource, Stack, toSeconds } from '@aws-cdk/cdk'; import { CfnStage } from './apigateway.generated'; import { Deployment } from './deployment'; import { IRestApi } from './restapi'; @@ -145,9 +145,9 @@ export interface MethodDeploymentOptions { * higher the TTL, the longer the response will be cached. * @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html * - * @default 300 + * @default Duration.minutes(5) */ - readonly cacheTtlSeconds?: number; + readonly cacheTtl?: Duration; /** * Indicates whether the cached responses are encrypted. @@ -224,7 +224,7 @@ export class Stage extends Resource { throttlingBurstLimit: props.throttlingBurstLimit, throttlingRateLimit: props.throttlingRateLimit, cachingEnabled: props.cachingEnabled, - cacheTtlSeconds: props.cacheTtlSeconds, + cacheTtl: props.cacheTtl, cacheDataEncrypted: props.cacheDataEncrypted }; @@ -256,7 +256,7 @@ export class Stage extends Resource { return { httpMethod, resourcePath, cacheDataEncrypted: options.cacheDataEncrypted, - cacheTtlInSeconds: options.cacheTtlSeconds, + cacheTtlInSeconds: toSeconds(options.cacheTtl), cachingEnabled: options.cachingEnabled, dataTraceEnabled: options.dataTraceEnabled, loggingLevel: options.loggingLevel, diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 48eac7de08339..e13c17c2974fd 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -91,8 +91,9 @@ }, "awslint": { "exclude": [ - "from-method:@aws-cdk/aws-apigateway.Resource" + "from-method:@aws-cdk/aws-apigateway.Resource", + "duration-prop-type:@aws-cdk/aws-apigateway.QuotaSettings.period" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts index b9c042022c9be..bb9fe106377c8 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts @@ -37,7 +37,7 @@ export interface StepScalingActionProps { * @see https://docs.aws.amazon.com/autoscaling/application/APIReference/API_StepScalingPolicyConfiguration.html * @default No cooldown period */ - readonly cooldownSec?: number; + readonly cooldown?: cdk.Duration; /** * Minimum absolute number to adjust capacity with as result of percentage scaling. @@ -86,7 +86,7 @@ export class StepScalingAction extends cdk.Construct { scalingTargetId: props.scalingTarget.scalableTargetId, stepScalingPolicyConfiguration: { adjustmentType: props.adjustmentType, - cooldown: props.cooldownSec, + cooldown: cdk.toSeconds(props.cooldown), minAdjustmentMagnitude: props.minAdjustmentMagnitude, metricAggregationType: props.metricAggregationType, stepAdjustments: cdk.Lazy.anyValue({ produce: () => this.adjustments }), @@ -190,4 +190,4 @@ export interface AdjustmentTier { * @default +Infinity */ readonly upperBound?: number; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts index 3b178dca42bab..7b91b513b7eff 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts @@ -35,7 +35,7 @@ export interface BasicStepScalingPolicyProps { * @see https://docs.aws.amazon.com/autoscaling/application/APIReference/API_StepScalingPolicyConfiguration.html * @default No cooldown period */ - readonly cooldownSec?: number; + readonly cooldown?: cdk.Duration; /** * Minimum absolute number to adjust capacity with as result of percentage scaling. @@ -86,7 +86,7 @@ export class StepScalingPolicy extends cdk.Construct { this.lowerAction = new StepScalingAction(this, 'LowerPolicy', { adjustmentType, - cooldownSec: props.cooldownSec, + cooldown: props.cooldown, metricAggregationType: aggregationTypeFromMetric(props.metric), minAdjustmentMagnitude: props.minAdjustmentMagnitude, scalingTarget: props.scalingTarget, @@ -102,7 +102,7 @@ export class StepScalingPolicy extends cdk.Construct { this.lowerAlarm = new cloudwatch.Alarm(this, 'LowerAlarm', { // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric.with({ period: cdk.Duration.minutes(1) }), alarmDescription: 'Lower threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.LessThanOrEqualToThreshold, evaluationPeriods: 1, @@ -116,7 +116,7 @@ export class StepScalingPolicy extends cdk.Construct { this.upperAction = new StepScalingAction(this, 'UpperPolicy', { adjustmentType, - cooldownSec: props.cooldownSec, + cooldown: props.cooldown, metricAggregationType: aggregationTypeFromMetric(props.metric), minAdjustmentMagnitude: props.minAdjustmentMagnitude, scalingTarget: props.scalingTarget, @@ -132,7 +132,7 @@ export class StepScalingPolicy extends cdk.Construct { this.upperAlarm = new cloudwatch.Alarm(this, 'UpperAlarm', { // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric.with({ period: cdk.Duration.minutes(1) }), alarmDescription: 'Upper threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanOrEqualToThreshold, evaluationPeriods: 1, diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts index 3e5a6733aeef4..4c653c5a721e8 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts @@ -37,14 +37,14 @@ export interface BaseTargetTrackingProps { * * @default - No scale in cooldown. */ - readonly scaleInCooldownSec?: number; + readonly scaleInCooldown?: cdk.Duration; /** * Period after a scale out activity completes before another scale out activity can start. * * @default - No scale out cooldown. */ - readonly scaleOutCooldownSec?: number; + readonly scaleOutCooldown?: cdk.Duration; } /** @@ -115,13 +115,6 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { throw new Error(`Exactly one of 'customMetric' or 'predefinedMetric' must be specified.`); } - if (props.scaleInCooldownSec !== undefined && props.scaleInCooldownSec < 0) { - throw new RangeError(`scaleInCooldown cannot be negative, got: ${props.scaleInCooldownSec}`); - } - if (props.scaleOutCooldownSec !== undefined && props.scaleOutCooldownSec < 0) { - throw new RangeError(`scaleOutCooldown cannot be negative, got: ${props.scaleOutCooldownSec}`); - } - super(scope, id); const resource = new CfnScalingPolicy(this, 'Resource', { @@ -135,8 +128,8 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { predefinedMetricType: props.predefinedMetric, resourceLabel: props.resourceLabel, } : undefined, - scaleInCooldown: props.scaleInCooldownSec, - scaleOutCooldown: props.scaleOutCooldownSec, + scaleInCooldown: cdk.toSeconds(props.scaleInCooldown), + scaleOutCooldown: cdk.toSeconds(props.scaleOutCooldown), targetValue: props.targetValue } }); @@ -171,4 +164,4 @@ export enum PredefinedMetric { SageMakerVariantInvocationsPerInstance = 'SageMakerVariantInvocationsPerInstance', ECSServiceAverageCPUUtilization = 'ECSServiceAverageCPUUtilization', ECSServiceAverageMemoryUtilization = 'ECSServiceAverageMemoryUtilization', -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index ba8f4363fb12a..88936e254aff9 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -5,7 +5,7 @@ import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); import iam = require('@aws-cdk/aws-iam'); import sns = require('@aws-cdk/aws-sns'); -import { AutoScalingRollingUpdate, Construct, Fn, IResource, Lazy, Resource, Tag } from '@aws-cdk/cdk'; +import { AutoScalingRollingUpdate, Construct, Duration, Fn, IResource, Lazy, Resource, Tag } from '@aws-cdk/cdk'; import { CfnAutoScalingGroup, CfnAutoScalingGroupProps, CfnLaunchConfiguration } from './autoscaling.generated'; import { BasicLifecycleHookProps, LifecycleHook } from './lifecycle-hook'; import { BasicScheduledActionProps, ScheduledAction } from './scheduled-action'; @@ -130,16 +130,16 @@ export interface CommonAutoScalingGroupProps { * * The maximum value is 43200 (12 hours). * - * @default 300 (5 minutes) + * @default Duration.minutes(5) */ - readonly resourceSignalTimeoutSec?: number; + readonly resourceSignalTimeout?: Duration; /** * Default scaling cooldown for this AutoScalingGroup * - * @default 300 (5 minutes) + * @default Duration.minutes(5) */ - readonly cooldownSeconds?: number; + readonly cooldown?: Duration; /** * Whether instances in the Auto Scaling Group should have public @@ -353,10 +353,6 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements constructor(scope: Construct, id: string, props: AutoScalingGroupProps) { super(scope, id); - if (props.cooldownSeconds !== undefined && props.cooldownSeconds < 0) { - throw new RangeError(`cooldownSeconds cannot be negative, got: ${props.cooldownSeconds}`); - } - this.securityGroup = new ec2.SecurityGroup(this, 'InstanceSecurityGroup', { vpc: props.vpc, allowAllOutbound: props.allowAllOutbound !== false @@ -404,7 +400,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements const { subnetIds } = props.vpc.selectSubnets(props.vpcSubnets); const asgProps: CfnAutoScalingGroupProps = { - cooldown: props.cooldownSeconds !== undefined ? `${props.cooldownSeconds}` : undefined, + cooldown: props.cooldown !== undefined ? `${props.cooldown}` : undefined, minSize: minCapacity.toString(), maxSize: maxCapacity.toString(), desiredCapacity: desiredCapacity.toString(), @@ -526,12 +522,12 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements }; } - if (props.resourceSignalCount !== undefined || props.resourceSignalTimeoutSec !== undefined) { + if (props.resourceSignalCount !== undefined || props.resourceSignalTimeout !== undefined) { this.autoScalingGroup.options.creationPolicy = { ...this.autoScalingGroup.options.creationPolicy, resourceSignal: { count: props.resourceSignalCount, - timeout: props.resourceSignalTimeoutSec !== undefined ? renderIsoDuration(props.resourceSignalTimeoutSec) : undefined, + timeout: props.resourceSignalTimeout, } }; } @@ -605,9 +601,9 @@ export interface RollingUpdateConfiguration { * PT#H#M#S, where each # is the number of hours, minutes, and seconds, * respectively). The maximum PauseTime is one hour (PT1H). * - * @default 300 if the waitOnResourceSignals property is true, otherwise 0 + * @default Duration.minutes(5) if the waitOnResourceSignals property is true, otherwise 0 */ - readonly pauseTimeSec?: number; + readonly pauseTime?: Duration; /** * Specifies whether the Auto Scaling group waits on signals from new instances during an update. @@ -651,14 +647,14 @@ export enum ScalingProcess { */ function renderRollingUpdateConfig(config: RollingUpdateConfiguration = {}): AutoScalingRollingUpdate { const waitOnResourceSignals = config.minSuccessfulInstancesPercent !== undefined ? true : false; - const pauseTimeSec = config.pauseTimeSec !== undefined ? config.pauseTimeSec : (waitOnResourceSignals ? 300 : 0); + const pauseTime = config.pauseTime || (waitOnResourceSignals ? Duration.minutes(5) : Duration.seconds(0)); return { maxBatchSize: config.maxBatchSize, minInstancesInService: config.minInstancesInService, minSuccessfulInstancesPercent: validatePercentage(config.minSuccessfulInstancesPercent), waitOnResourceSignals, - pauseTime: renderIsoDuration(pauseTimeSec), + pauseTime: pauseTime && pauseTime.toISOString(), suspendProcesses: config.suspendProcesses !== undefined ? config.suspendProcesses : // Recommended list of processes to suspend from here: // https://aws.amazon.com/premiumsupport/knowledge-center/auto-scaling-group-rolling-updates/ @@ -667,31 +663,6 @@ function renderRollingUpdateConfig(config: RollingUpdateConfiguration = {}): Aut }; } -/** - * Render a number of seconds to a PTnX string. - */ -function renderIsoDuration(seconds: number): string { - const ret: string[] = []; - - if (seconds === 0) { - return 'PT0S'; - } - - if (seconds >= 3600) { - ret.push(`${Math.floor(seconds / 3600)}H`); - seconds %= 3600; - } - if (seconds >= 60) { - ret.push(`${Math.floor(seconds / 60)}M`); - seconds %= 60; - } - if (seconds > 0) { - ret.push(`${seconds}S`); - } - - return 'PT' + ret.join(''); -} - function validatePercentage(x?: number): number | undefined { if (x === undefined || (0 <= x && x <= 100)) { return x; } throw new Error(`Expected: a percentage 0..100, got: ${x}`); diff --git a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts index 5859f2b969584..4292008bc49c0 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts @@ -1,5 +1,5 @@ import iam = require('@aws-cdk/aws-iam'); -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Resource, toSeconds } from '@aws-cdk/cdk'; import { IAutoScalingGroup } from './auto-scaling-group'; import { CfnLifecycleHook } from './autoscaling.generated'; import { ILifecycleHookTarget } from './lifecycle-hook-target'; @@ -29,7 +29,7 @@ export interface BasicLifecycleHookProps { * * @default - No heartbeat timeout. */ - readonly heartbeatTimeoutSec?: number; + readonly heartbeatTimeout?: Duration; /** * The state of the Amazon EC2 instance to which you want to attach the lifecycle hook. @@ -103,7 +103,7 @@ export class LifecycleHook extends Resource implements ILifecycleHook { const resource = new CfnLifecycleHook(this, 'Resource', { autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, defaultResult: props.defaultResult, - heartbeatTimeout: props.heartbeatTimeoutSec, + heartbeatTimeout: toSeconds(props.heartbeatTimeout), lifecycleHookName: props.lifecycleHookName, lifecycleTransition: props.lifecycleTransition, notificationMetadata: props.notificationMetadata, diff --git a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts index a0eeed3f0fca4..892010ec9d247 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts @@ -16,14 +16,14 @@ export interface StepScalingActionProps { * * @default The default cooldown configured on the AutoScalingGroup */ - readonly cooldownSeconds?: number; + readonly cooldown?: cdk.Duration; /** * Estimated time until a newly launched instance can send metrics to CloudWatch. * * @default Same as the cooldown */ - readonly estimatedInstanceWarmupSeconds?: number; + readonly estimatedInstanceWarmup?: cdk.Duration; /** * How the adjustment numbers are interpreted @@ -73,8 +73,8 @@ export class StepScalingAction extends cdk.Construct { const resource = new CfnScalingPolicy(this, 'Resource', { policyType: 'StepScaling', autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, - cooldown: props.cooldownSeconds !== undefined ? `${props.cooldownSeconds}` : undefined, - estimatedInstanceWarmup: props.estimatedInstanceWarmupSeconds, + cooldown: props.cooldown && props.cooldown.toSeconds().toString(), + estimatedInstanceWarmup: cdk.toSeconds(props.estimatedInstanceWarmup), adjustmentType: props.adjustmentType, minAdjustmentMagnitude: props.minAdjustmentMagnitude, metricAggregationType: props.metricAggregationType, @@ -178,4 +178,4 @@ export interface AdjustmentTier { * @default +Infinity */ readonly upperBound?: number; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts index 3e3a126a4391d..df0abdb09cf56 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts @@ -29,14 +29,14 @@ export interface BasicStepScalingPolicyProps { * * @default Default cooldown period on your AutoScalingGroup */ - readonly cooldownSeconds?: number; + readonly cooldown?: cdk.Duration; /** * Estimated time until a newly launched instance can send metrics to CloudWatch. * * @default Same as the cooldown */ - readonly estimatedInstanceWarmupSeconds?: number; + readonly estimatedInstanceWarmup?: cdk.Duration; /** * Minimum absolute number to adjust capacity with as result of percentage scaling. @@ -87,7 +87,7 @@ export class StepScalingPolicy extends cdk.Construct { this.lowerAction = new StepScalingAction(this, 'LowerPolicy', { adjustmentType: props.adjustmentType, - cooldownSeconds: props.cooldownSeconds, + cooldown: props.cooldown, metricAggregationType: aggregationTypeFromMetric(props.metric), minAdjustmentMagnitude: props.minAdjustmentMagnitude, autoScalingGroup: props.autoScalingGroup, @@ -103,7 +103,7 @@ export class StepScalingPolicy extends cdk.Construct { this.lowerAlarm = new cloudwatch.Alarm(this, 'LowerAlarm', { // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric.with({ period: cdk.Duration.minutes(1) }), alarmDescription: 'Lower threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.LessThanOrEqualToThreshold, evaluationPeriods: 1, @@ -117,7 +117,7 @@ export class StepScalingPolicy extends cdk.Construct { this.upperAction = new StepScalingAction(this, 'UpperPolicy', { adjustmentType: props.adjustmentType, - cooldownSeconds: props.cooldownSeconds, + cooldown: props.cooldown, metricAggregationType: aggregationTypeFromMetric(props.metric), minAdjustmentMagnitude: props.minAdjustmentMagnitude, autoScalingGroup: props.autoScalingGroup, @@ -133,7 +133,7 @@ export class StepScalingPolicy extends cdk.Construct { this.upperAlarm = new cloudwatch.Alarm(this, 'UpperAlarm', { // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric.with({ period: cdk.Duration.minutes(1) }), alarmDescription: 'Upper threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanOrEqualToThreshold, evaluationPeriods: 1, diff --git a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts index 7074f87d12882..29c65923dace5 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts @@ -29,14 +29,14 @@ export interface BaseTargetTrackingProps { * * @default - The default cooldown configured on the AutoScalingGroup. */ - readonly cooldownSeconds?: number; + readonly cooldown?: cdk.Duration; /** * Estimated time until a newly launched instance can send metrics to CloudWatch. * * @default - Same as the cooldown. */ - readonly estimatedInstanceWarmupSeconds?: number; + readonly estimatedInstanceWarmup?: cdk.Duration; } /** @@ -113,14 +113,6 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { throw new Error(`Exactly one of 'customMetric' or 'predefinedMetric' must be specified.`); } - if (props.cooldownSeconds !== undefined && props.cooldownSeconds < 0) { - throw new RangeError(`cooldownSeconds cannot be negative, got: ${props.cooldownSeconds}`); - } - - if (props.estimatedInstanceWarmupSeconds !== undefined && props.estimatedInstanceWarmupSeconds < 0) { - throw new RangeError(`estimatedInstanceWarmupSeconds cannot be negative, got: ${props.estimatedInstanceWarmupSeconds}`); - } - if (props.predefinedMetric === PredefinedMetric.ALBRequestCountPerTarget && !props.resourceLabel) { throw new Error('When tracking the ALBRequestCountPerTarget metric, the ALB identifier must be supplied in resourceLabel'); } @@ -130,8 +122,8 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { this.resource = new CfnScalingPolicy(this, 'Resource', { policyType: 'TargetTrackingScaling', autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, - cooldown: props.cooldownSeconds !== undefined ? `${props.cooldownSeconds}` : undefined, - estimatedInstanceWarmup: props.estimatedInstanceWarmupSeconds, + cooldown: props.cooldown && props.cooldown.toSeconds().toString(), + estimatedInstanceWarmup: cdk.toSeconds(props.estimatedInstanceWarmup), targetTrackingConfiguration: { customizedMetricSpecification: renderCustomMetric(props.customMetric), disableScaleIn: props.disableScaleIn, diff --git a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts index 72fe66b8dc838..2c5237862f27d 100644 --- a/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/test/test.auto-scaling-group.ts @@ -294,7 +294,7 @@ export = { updateType: autoscaling.UpdateType.RollingUpdate, rollingUpdateConfiguration: { minSuccessfulInstancesPercent: 50, - pauseTimeSec: 345 + pauseTime: cdk.Duration.seconds(345) } }); @@ -324,7 +324,7 @@ export = { machineImage: new ec2.AmazonLinuxImage(), vpc, resourceSignalCount: 5, - resourceSignalTimeoutSec: 666 + resourceSignalTimeout: cdk.Duration.seconds(666) }); // THEN @@ -379,7 +379,7 @@ export = { updateType: autoscaling.UpdateType.RollingUpdate, rollingUpdateConfiguration: { minSuccessfulInstancesPercent: 50, - pauseTimeSec: 345 + pauseTime: cdk.Duration.seconds(345) }, }); asg.node.applyAspect(new cdk.Tag('superfood', 'acai')); diff --git a/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts b/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts index e329df9ec4263..cfca14a248a9b 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts +++ b/packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts @@ -51,7 +51,7 @@ export class DnsValidatedCertificate extends cdk.Resource implements ICertificat code: lambda.Code.asset(path.resolve(__dirname, '..', 'lambda-packages', 'dns_validated_certificate_handler', 'lib')), handler: 'index.certificateRequestHandler', runtime: lambda.Runtime.NodeJS810, - timeout: 15 * 60 // 15 minutes + timeout: cdk.Duration.minutes(15) }); requestorFunction.addToRolePolicy( new iam.PolicyStatement() diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts index acc41d471a5b7..f085f70568acf 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.trivial-lambda-resource.ts @@ -27,7 +27,7 @@ class DemoResource extends cdk.Construct { // This makes the demo only work as top-level TypeScript program, but that's fine for now code: new lambda.InlineCode(fs.readFileSync('integ.trivial-lambda-provider.py', { encoding: 'utf-8' })), handler: 'index.main', - timeout: 300, + timeout: cdk.Duration.minutes(5), runtime: lambda.Runtime.Python27, })), properties: props diff --git a/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts b/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts index 29cdb2ff2d22d..3623c0335916c 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts @@ -149,7 +149,7 @@ class TestCustomResource extends cdk.Construct { code: new lambda.InlineCode('def hello(): pass'), runtime: lambda.Runtime.Python27, handler: 'index.hello', - timeout: 300, + timeout: cdk.Duration.minutes(5), }); new CustomResource(this, 'Resource', { diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts index 6fa422aee3775..b8fa20585dd9f 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts @@ -193,9 +193,9 @@ export interface CustomOriginConfig { /** * The keep alive timeout when making calls in seconds. * - * @default 5 + * @default Duration.seconds(5) */ - readonly originKeepaliveTimeoutSeconds?: number, + readonly originKeepaliveTimeout?: cdk.Duration, /** * The protocol (http or https) policy to use when interacting with the origin. @@ -207,9 +207,9 @@ export interface CustomOriginConfig { /** * The read timeout when calling the origin in seconds * - * @default 30 + * @default Duration.seconds(30) */ - readonly originReadTimeoutSeconds?: number + readonly originReadTimeout?: cdk.Duration /** * The SSL versions to use when interacting with the origin. @@ -299,7 +299,7 @@ export interface Behavior { * @default 86400 (1 day) * */ - readonly defaultTtlSeconds?: number; + readonly defaultTtl?: cdk.Duration; /** * The method this CloudFront distribution responds do. @@ -334,37 +334,18 @@ export interface Behavior { * The minimum amount of time that you want objects to stay in the cache * before CloudFront queries your origin. */ - readonly minTtlSeconds?: number; + readonly minTtl?: cdk.Duration; /** * The max amount of time you want objects to stay in the cache * before CloudFront queries your origin. * - * @default 31536000 (one year) + * @default Duration.seconds(31536000) (one year) */ - readonly maxTtlSeconds?: number; + readonly maxTtl?: cdk.Duration; } -export interface ErrorConfiguration { - /** - * The error code matched from the origin - */ - readonly originErrorCode: number; - /** - * The error code that is sent to the caller. - */ - readonly respondWithErrorCode: number; - /** - * The path to service instead - */ - readonly respondWithPage: string; - /** - * How long before this error is retried. - */ - readonly cacheTtl?: number; -} - export interface CloudFrontWebDistributionProps { /** @@ -582,8 +563,8 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib ? { httpPort: originConfig.customOriginSource.httpPort || 80, httpsPort: originConfig.customOriginSource.httpsPort || 443, - originKeepaliveTimeout: originConfig.customOriginSource.originKeepaliveTimeoutSeconds || 5, - originReadTimeout: originConfig.customOriginSource.originReadTimeoutSeconds || 30, + originKeepaliveTimeout: cdk.toSeconds(originConfig.customOriginSource.originKeepaliveTimeout) || 5, + originReadTimeout: cdk.toSeconds(originConfig.customOriginSource.originReadTimeout) || 30, originProtocolPolicy: originConfig.customOriginSource.originProtocolPolicy || OriginProtocolPolicy.HttpsOnly, originSslProtocols: originConfig.customOriginSource.allowedOriginSSLVersions || [OriginSslPolicy.TLSv1_2] } @@ -680,10 +661,10 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib allowedMethods: this.METHOD_LOOKUP_MAP[input.allowedMethods || CloudFrontAllowedMethods.GET_HEAD], cachedMethods: this.METHOD_LOOKUP_MAP[input.cachedMethods || CloudFrontAllowedCachedMethods.GET_HEAD], compress: input.compress, - defaultTtl: input.defaultTtlSeconds, + defaultTtl: cdk.toSeconds(input.defaultTtl), forwardedValues: input.forwardedValues || { queryString: false, cookies: { forward: "none" } }, - maxTtl: input.maxTtlSeconds, - minTtl: input.minTtlSeconds, + maxTtl: cdk.toSeconds(input.maxTtl), + minTtl: cdk.toSeconds(input.minTtl), trustedSigners: input.trustedSigners, targetOriginId: input.targetOriginId, viewerProtocolPolicy: protoPolicy || ViewerProtocolPolicy.RedirectToHTTPS, diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 039d6e2009b20..5b64f9eb0a2a0 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -88,8 +88,14 @@ "@aws-cdk/aws-s3": "^0.34.0", "@aws-cdk/cdk": "^0.34.0" }, + "awslint": { + "exclude": [ + "duration-prop-type:@aws-cdk/aws-cloudtrail.TrailProps.cloudWatchLogsRetentionTimeDays", + "duration-prop-name:@aws-cdk/aws-cloudtrail.TrailProps.cloudWatchLogsRetentionTimeDays" + ] + }, "engines": { "node": ">= 8.10.0" }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts index 52097fc45b6e6..f56abaef6a39f 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts @@ -146,7 +146,7 @@ export class Alarm extends Resource implements IAlarm { this.metric = props.metric; this.annotation = { // tslint:disable-next-line:max-line-length - label: `${this.metric.label || this.metric.metricName} ${OPERATOR_SYMBOLS[comparisonOperator]} ${props.threshold} for ${props.evaluationPeriods} datapoints within ${describePeriod(props.evaluationPeriods * props.metric.periodSec)}`, + label: `${this.metric.label || this.metric.metricName} ${OPERATOR_SYMBOLS[comparisonOperator]} ${props.threshold} for ${props.evaluationPeriods} datapoints within ${describePeriod(props.evaluationPeriods * props.metric.period.toSeconds())}`, value: props.threshold, }; } @@ -235,7 +235,7 @@ function metricJson(metric: Metric): AlarmMetricJson { dimensions: dims.length > 0 ? dims : undefined, namespace: metric.namespace, metricName: metric.metricName, - period: metric.periodSec, + period: metric.period.toSeconds(), statistic: stat.type === 'simple' ? stat.statistic : undefined, extendedStatistic: stat.type === 'percentile' ? 'p' + stat.percentile : undefined, unit: metric.unit @@ -245,7 +245,7 @@ function metricJson(metric: Metric): AlarmMetricJson { /** * Properties used to construct the Metric identifying part of an Alarm */ -export interface AlarmMetricJson { +interface AlarmMetricJson { /** * The dimensions to apply to the alarm */ diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts index 249d0c0a739ed..de27848880fc0 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts @@ -315,7 +315,7 @@ function metricJson(metric: Metric, yAxis: string): any[] { yAxis, label: metric.label, color: metric.color, - period: metric.periodSec, + period: metric.period.toSeconds(), stat: stat.type === 'simple' ? stat.statistic : 'p' + stat.percentile.toString(), }); diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts index da81e2022beda..e18bb750b64f1 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts @@ -29,11 +29,9 @@ export interface MetricProps { /** * The period over which the specified statistic is applied. * - * Specify time in seconds, in multiples of 60. - * - * @default 300 + * @default Duration.minutes(5) */ - readonly periodSec?: number; + readonly period?: cdk.Duration; /** * What function to use for aggregating. @@ -98,23 +96,22 @@ export class Metric { public readonly dimensions?: DimensionHash; public readonly namespace: string; public readonly metricName: string; - public readonly periodSec: number; + public readonly period: cdk.Duration; public readonly statistic: string; public readonly unit?: Unit; public readonly label?: string; public readonly color?: string; constructor(props: MetricProps) { - if (props.periodSec !== undefined - && props.periodSec !== 1 && props.periodSec !== 5 && props.periodSec !== 10 && props.periodSec !== 30 - && props.periodSec % 60 !== 0) { - throw new Error("'periodSec' must be 1, 5, 10, 30, or a multiple of 60"); + this.period = props.period || cdk.Duration.minutes(5); + const periodSec = this.period.toSeconds(); + if (periodSec !== 1 && periodSec !== 5 && periodSec !== 10 && periodSec !== 30 && periodSec % 60 !== 0) { + throw new Error(`'period' must be 1, 5, 10, 30, or a multiple of 60 seconds, received ${props.period}`); } this.dimensions = props.dimensions; this.namespace = props.namespace; this.metricName = props.metricName; - this.periodSec = props.periodSec !== undefined ? props.periodSec : 300; // Try parsing, this will throw if it's not a valid stat this.statistic = normalizeStatistic(props.statistic || "Average"); this.label = props.label; @@ -134,7 +131,7 @@ export class Metric { dimensions: ifUndefined(props.dimensions, this.dimensions), namespace: this.namespace, metricName: this.metricName, - periodSec: ifUndefined(props.periodSec, this.periodSec), + period: ifUndefined(props.period, this.period), statistic: ifUndefined(props.statistic, this.statistic), unit: ifUndefined(props.unit, this.unit), label: ifUndefined(props.label, this.label), @@ -152,7 +149,7 @@ export class Metric { return new Alarm(scope, id, { metric: this.with({ statistic: props.statistic, - periodSec: props.periodSec, + period: props.period, }), alarmName: props.alarmName, alarmDescription: props.alarmDescription, @@ -255,11 +252,9 @@ export interface MetricOptions { /** * The period over which the specified statistic is applied. * - * Specify time in seconds, in multiples of 60. - * - * @default 300 + * @default Duration.minutes(5) */ - readonly periodSec?: number; + readonly period?: cdk.Duration; /** * What function to use for aggregating. @@ -300,11 +295,9 @@ export interface MetricAlarmProps { /** * The period over which the specified statistic is applied. * - * Specify time in seconds, in multiples of 60. - * - * @default 300 + * @default Duration.minutes(5) */ - readonly periodSec?: number; + readonly period?: cdk.Duration; /** * What function to use for aggregating. diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts index bb219e2b76f77..3beab9756f2b8 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts @@ -1,5 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Alarm, IAlarm, IAlarmAction, Metric } from '../lib'; @@ -95,7 +95,7 @@ export = { threshold: 1000, evaluationPeriods: 2, statistic: 'min', - periodSec: 10, + period: Duration.seconds(10), }); // THEN diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 381b2c7c2ffff..d1bbffd04161e 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -6,7 +6,7 @@ import ecr = require('@aws-cdk/aws-ecr'); import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); -import { Aws, Construct, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Aws, Construct, Duration, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { BuildArtifacts, CodePipelineBuildArtifacts, NoBuildArtifacts } from './artifacts'; import { Cache } from './cache'; import { CfnProject } from './codebuild.generated'; @@ -435,9 +435,9 @@ export interface CommonProjectProps { * not complete. For valid values, see the timeoutInMinutes field in the AWS * CodeBuild User Guide. * - * @default 60 + * @default Duration.hours(1) */ - readonly timeout?: number; + readonly timeout?: Duration; /** * Additional environment variables to add to the build environment. @@ -704,7 +704,7 @@ export class Project extends ProjectBase { badgeEnabled: props.badge, cache: cache._toCloudFormation(), name: this.physicalName.value, - timeoutInMinutes: props.timeout, + timeoutInMinutes: props.timeout && props.timeout.toMinutes(), secondarySources: Lazy.anyValue({ produce: () => this.renderSecondarySources() }), secondaryArtifacts: Lazy.anyValue({ produce: () => this.renderSecondaryArtifacts() }), triggers: this.source._buildTriggers(), diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index c18b6e2a9eb64..d26383f8ce095 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -108,8 +108,10 @@ }, "awslint": { "exclude": [ - "construct-ctor-props-optional:@aws-cdk/aws-codebuild.Project" + "construct-ctor-props-optional:@aws-cdk/aws-codebuild.Project", + "duration-prop-type:@aws-cdk/aws-codebuild.PhaseChangeEvent.completedPhaseDurationSeconds", + "duration-prop-name:@aws-cdk/aws-codebuild.PhaseChangeEvent.completedPhaseDurationSeconds" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts index 9badd9435488e..5ea4d04dcae64 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -770,7 +770,7 @@ export = { name: 'some_name', bucket, }), - timeout: 123, + timeout: cdk.Duration.minutes(123), }); expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', { diff --git a/packages/@aws-cdk/aws-config/lib/managed-rules.ts b/packages/@aws-cdk/aws-config/lib/managed-rules.ts index d6f6947128e03..e1d72bc935a9c 100644 --- a/packages/@aws-cdk/aws-config/lib/managed-rules.ts +++ b/packages/@aws-cdk/aws-config/lib/managed-rules.ts @@ -1,6 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import sns = require('@aws-cdk/aws-sns'); -import { Construct, Lazy, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Lazy, Stack } from '@aws-cdk/cdk'; import { ManagedRule, RuleProps } from './rule'; /** @@ -10,14 +10,14 @@ export interface AccessKeysRotatedProps extends RuleProps { /** * The maximum number of days within which the access keys must be rotated. * - * @default 90 days + * @default Duration.days(90) */ - readonly maxDays?: number; + readonly maxAge?: Duration; } /** * Checks whether the active access keys are rotated within the number of days - * specified in `maxDays`. + * specified in `maxAge`. * * @see https://docs.aws.amazon.com/config/latest/developerguide/access-keys-rotated.html * @@ -29,9 +29,9 @@ export class AccessKeysRotated extends ManagedRule { ...props, identifier: 'ACCESS_KEYS_ROTATED', inputParameters: { - ...props.maxDays + ...props.maxAge ? { - maxAccessKeyAge: props.maxDays + maxAccessKeyAge: props.maxAge.toDays() } : {} } diff --git a/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts b/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts index 5747247d6d1b9..e9ffd05337278 100644 --- a/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts +++ b/packages/@aws-cdk/aws-dynamodb-global/lib/global-table-coordinator.ts @@ -17,7 +17,7 @@ export class GlobalTableCoordinator extends cdk.Stack { description: "Lambda to make DynamoDB a global table", handler: "index.handler", runtime: lambda.Runtime.NodeJS810, - timeout: 300, + timeout: cdk.Duration.minutes(5), uuid: "D38B65A6-6B54-4FB6-9BAD-9CD40A6DAC12", }); diff --git a/packages/@aws-cdk/aws-dynamodb/lib/scalable-table-attribute.ts b/packages/@aws-cdk/aws-dynamodb/lib/scalable-table-attribute.ts index a0fb5e83bf946..cf95747f7768f 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/scalable-table-attribute.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/scalable-table-attribute.ts @@ -27,8 +27,8 @@ export class ScalableTableAttribute extends appscaling.BaseScalableAttribute { super.doScaleToTrackMetric('Tracking', { policyName: props.policyName, disableScaleIn: props.disableScaleIn, - scaleInCooldownSec: props.scaleInCooldownSec, - scaleOutCooldownSec: props.scaleOutCooldownSec, + scaleInCooldown: props.scaleInCooldown, + scaleOutCooldown: props.scaleOutCooldown, targetValue: props.targetUtilizationPercent, predefinedMetric, }); diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpn.ts b/packages/@aws-cdk/aws-ec2/test/test.vpn.ts index 97e6c321ec56d..5e805f6d04c37 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpn.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpn.ts @@ -1,5 +1,5 @@ import { expect, haveResource, } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Vpc, VpnConnection } from '../lib'; @@ -278,7 +278,7 @@ export = { dimensions: { VpnId: { Ref: 'VpcNetworkVpnA476C58D' } }, namespace: 'AWS/VPN', metricName: 'TunnelState', - periodSec: 300, + period: Duration.minutes(5), statistic: 'Average' }); @@ -293,7 +293,7 @@ export = { test.deepEqual(stack.resolve(VpnConnection.metricAllTunnelDataOut()), { namespace: 'AWS/VPN', metricName: 'TunnelDataOut', - periodSec: 300, + period: Duration.minutes(5), statistic: 'Sum' }); diff --git a/packages/@aws-cdk/aws-ecr/lib/lifecycle.ts b/packages/@aws-cdk/aws-ecr/lib/lifecycle.ts index d86bb540c7e7b..3fba6351941f0 100644 --- a/packages/@aws-cdk/aws-ecr/lib/lifecycle.ts +++ b/packages/@aws-cdk/aws-ecr/lib/lifecycle.ts @@ -1,3 +1,5 @@ +import { Duration } from '@aws-cdk/cdk'; + /** * An ECR life cycle rule */ @@ -50,11 +52,11 @@ export interface LifecycleRule { readonly maxImageCount?: number; /** - * The maximum age of images to retain + * The maximum age of images to retain. The value must represent a number of days. * - * Specify exactly one of maxImageCount and maxImageAgeDays. + * Specify exactly one of maxImageCount and maxImageAge. */ - readonly maxImageAgeDays?: number; + readonly maxImageAge?: Duration; } /** diff --git a/packages/@aws-cdk/aws-ecr/lib/repository.ts b/packages/@aws-cdk/aws-ecr/lib/repository.ts index 9fa3186fedf3a..ec56b729cf3f2 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -385,8 +385,8 @@ export class Repository extends RepositoryBase { if (rule.tagStatus !== TagStatus.Tagged && rule.tagPrefixList !== undefined) { throw new Error('tagPrefixList can only be specified when tagStatus is set to Tagged'); } - if ((rule.maxImageAgeDays !== undefined) === (rule.maxImageCount !== undefined)) { - throw new Error(`Life cycle rule must contain exactly one of 'maxImageAgeDays' and 'maxImageCount', got: ${JSON.stringify(rule)}`); + if ((rule.maxImageAge !== undefined) === (rule.maxImageCount !== undefined)) { + throw new Error(`Life cycle rule must contain exactly one of 'maxImageAge' and 'maxImageCount', got: ${JSON.stringify(rule)}`); } if (rule.tagStatus === TagStatus.Any && this.lifecycleRules.filter(r => r.tagStatus === TagStatus.Any).length > 0) { @@ -470,9 +470,9 @@ function renderLifecycleRule(rule: LifecycleRule) { selection: { tagStatus: rule.tagStatus || TagStatus.Any, tagPrefixList: rule.tagPrefixList, - countType: rule.maxImageAgeDays !== undefined ? CountType.SinceImagePushed : CountType.ImageCountMoreThan, - countNumber: rule.maxImageAgeDays !== undefined ? rule.maxImageAgeDays : rule.maxImageCount, - countUnit: rule.maxImageAgeDays !== undefined ? 'days' : undefined, + countType: rule.maxImageAge !== undefined ? CountType.SinceImagePushed : CountType.ImageCountMoreThan, + countNumber: rule.maxImageAge !== undefined ? rule.maxImageAge.toDays() : rule.maxImageCount, + countUnit: rule.maxImageAge !== undefined ? 'days' : undefined, }, action: { type: 'expire' diff --git a/packages/@aws-cdk/aws-ecr/test/test.repository.ts b/packages/@aws-cdk/aws-ecr/test/test.repository.ts index ebbaf4aa44504..8affb900c9275 100644 --- a/packages/@aws-cdk/aws-ecr/test/test.repository.ts +++ b/packages/@aws-cdk/aws-ecr/test/test.repository.ts @@ -52,7 +52,7 @@ export = { // WHEN const repo = new ecr.Repository(stack, 'Repo'); repo.addLifecycleRule({ - maxImageAgeDays: 5, + maxImageAge: cdk.Duration.days(5), }); // THEN diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index ff2c41da4eaa8..423f048993954 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -4,8 +4,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); import iam = require('@aws-cdk/aws-iam'); import cloudmap = require('@aws-cdk/aws-servicediscovery'); -import { IResource, Lazy, Resource, Stack } from '@aws-cdk/cdk'; -import cdk = require('@aws-cdk/cdk'); +import { Construct, Duration, Fn, IResource, Lazy, Resource, Stack, toSeconds } from '@aws-cdk/cdk'; import { NetworkMode, TaskDefinition } from '../base/task-definition'; import { ICluster } from '../cluster'; import { CfnService } from '../ecs.generated'; @@ -66,7 +65,7 @@ export interface BaseServiceProps { * * @default ??? FIXME */ - readonly healthCheckGracePeriodSeconds?: number; + readonly healthCheckGracePeriod?: Duration; /** * Options for enabling AWS Cloud Map service discovery for the service @@ -133,7 +132,7 @@ export abstract class BaseService extends Resource private readonly resource: CfnService; private scalableTaskCount?: ScalableTaskCount; - constructor(scope: cdk.Construct, + constructor(scope: Construct, id: string, props: BaseServiceProps, additionalProps: any, @@ -151,7 +150,7 @@ export abstract class BaseService extends Resource maximumPercent: props.maximumPercent || 200, minimumHealthyPercent: props.minimumHealthyPercent === undefined ? 50 : props.minimumHealthyPercent }, - healthCheckGracePeriodSeconds: props.healthCheckGracePeriodSeconds, + healthCheckGracePeriodSeconds: toSeconds(props.healthCheckGracePeriod), /* role: never specified, supplanted by Service Linked Role */ networkConfiguration: Lazy.anyValue({ produce: () => this.networkConfiguration }), serviceRegistries: Lazy.anyValue({ produce: () => this.serviceRegistries }), @@ -164,7 +163,7 @@ export abstract class BaseService extends Resource // are enabled for the principal in a given region. const longArnEnabled = props.longArnEnabled !== undefined ? props.longArnEnabled : false; this.serviceName = longArnEnabled - ? cdk.Fn.select(2, cdk.Fn.split('/', this.serviceArn)) + ? Fn.select(2, Fn.split('/', this.serviceArn)) : this.resource.serviceName; this.clusterName = clusterName; @@ -411,7 +410,7 @@ export interface ServiceDiscoveryOptions { * * @default 60 */ - readonly dnsTtlSec?: number; + readonly dnsTtl?: Duration; /** * The number of 30-second intervals that you want Cloud Map to wait after receiving an diff --git a/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts b/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts index 54a7c5e80fa2e..73fccecfa3940 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts @@ -40,8 +40,8 @@ export class ScalableTaskCount extends appscaling.BaseScalableAttribute { policyName: props.policyName, disableScaleIn: props.disableScaleIn, targetValue: props.targetUtilizationPercent, - scaleInCooldownSec: props.scaleInCooldownSec, - scaleOutCooldownSec: props.scaleOutCooldownSec + scaleInCooldown: props.scaleInCooldown, + scaleOutCooldown: props.scaleOutCooldown }); } @@ -54,8 +54,8 @@ export class ScalableTaskCount extends appscaling.BaseScalableAttribute { targetValue: props.targetUtilizationPercent, policyName: props.policyName, disableScaleIn: props.disableScaleIn, - scaleInCooldownSec: props.scaleInCooldownSec, - scaleOutCooldownSec: props.scaleOutCooldownSec + scaleInCooldown: props.scaleInCooldown, + scaleOutCooldown: props.scaleOutCooldown }); } @@ -72,8 +72,8 @@ export class ScalableTaskCount extends appscaling.BaseScalableAttribute { targetValue: props.requestsPerTarget, policyName: props.policyName, disableScaleIn: props.disableScaleIn, - scaleInCooldownSec: props.scaleInCooldownSec, - scaleOutCooldownSec: props.scaleOutCooldownSec + scaleInCooldown: props.scaleInCooldown, + scaleOutCooldown: props.scaleOutCooldown }); } @@ -86,8 +86,8 @@ export class ScalableTaskCount extends appscaling.BaseScalableAttribute { targetValue: props.targetValue, policyName: props.policyName, disableScaleIn: props.disableScaleIn, - scaleInCooldownSec: props.scaleInCooldownSec, - scaleOutCooldownSec: props.scaleOutCooldownSec, + scaleInCooldown: props.scaleInCooldown, + scaleOutCooldown: props.scaleOutCooldown, }); } } diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index cf9812bbe887a..2c5d2d90d6723 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -3,7 +3,7 @@ import cloudwatch = require ('@aws-cdk/aws-cloudwatch'); import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import cloudmap = require('@aws-cdk/aws-servicediscovery'); -import { Construct, IResource, Resource, SSMParameterProvider, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Resource, SSMParameterProvider, Stack } from '@aws-cdk/cdk'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { CfnCluster } from './ecs.generated'; @@ -167,11 +167,11 @@ export class Cluster extends Resource implements ICluster { ).addAllResources()); // 0 disables, otherwise forward to underlying implementation which picks the sane default - if (options.taskDrainTimeSeconds !== 0) { + if (!options.taskDrainTime || options.taskDrainTime.toSeconds() !== 0) { new InstanceDrainHook(autoScalingGroup, 'DrainECSHook', { autoScalingGroup, cluster: this, - drainTimeSec: options.taskDrainTimeSeconds + drainTime: options.taskDrainTime }); } } @@ -442,9 +442,9 @@ export interface AddAutoScalingGroupCapacityOptions { * * Set to 0 to disable task draining. * - * @default 300 + * @default Duration.minutes(5) */ - readonly taskDrainTimeSeconds?: number; + readonly taskDrainTime?: Duration; } /** diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index d05de2d082f62..daca16ee46e25 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -434,9 +434,9 @@ export interface HealthCheck { * * You may specify between 5 and 300 seconds. * - * @default 30 + * @default Duration.seconds(30) */ - readonly intervalSeconds?: number; + readonly interval?: cdk.Duration; /** * Number of times to retry a failed health check before the container is considered unhealthy. @@ -454,16 +454,16 @@ export interface HealthCheck { * * @default No start period */ - readonly startPeriod?: number; + readonly startPeriod?: cdk.Duration; /** * The time period in seconds to wait for a health check to succeed before it is considered a failure. * * You may specify between 2 and 60 seconds. * - * @default 5 + * @default Duration.seconds(5) */ - readonly timeout?: number; + readonly timeout?: cdk.Duration; } function renderKV(env: { [key: string]: string }, keyName: string, valueName: string): any { @@ -477,10 +477,10 @@ function renderKV(env: { [key: string]: string }, keyName: string, valueName: st function renderHealthCheck(hc: HealthCheck): CfnTaskDefinition.HealthCheckProperty { return { command: getHealthCheckCommand(hc), - interval: hc.intervalSeconds !== undefined ? hc.intervalSeconds : 30, + interval: hc.interval != null ? hc.interval.toSeconds() : 30, retries: hc.retries !== undefined ? hc.retries : 3, - startPeriod: hc.startPeriod, - timeout: hc.timeout !== undefined ? hc.timeout : 5, + startPeriod: cdk.toSeconds(hc.startPeriod), + timeout: hc.timeout !== undefined ? hc.timeout.toSeconds() : 5, }; } diff --git a/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts b/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts index 87fd362a0eccb..83434861e5e1d 100644 --- a/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts +++ b/packages/@aws-cdk/aws-ecs/lib/drain-hook/instance-drain-hook.ts @@ -28,11 +28,11 @@ export interface InstanceDrainHookProps { /** * How many seconds to give tasks to drain before the instance is terminated anyway * - * Must be between 0 and 900. + * Must be between 0 and 15 minutes. * - * @default 900 + * @default Duration.minutes(15) */ - drainTimeSec?: number; + drainTime?: cdk.Duration; } /** @@ -42,11 +42,7 @@ export class InstanceDrainHook extends cdk.Construct { constructor(scope: cdk.Construct, id: string, props: InstanceDrainHookProps) { super(scope, id); - const drainTimeSeconds = props.drainTimeSec !== undefined ? props.drainTimeSec : 300; - - if (drainTimeSeconds < 0 || drainTimeSeconds > 900) { - throw new Error(`Drain time must be between 0 and 900 seconds, got: ${drainTimeSeconds}`); - } + const drainTime = props.drainTime || cdk.Duration.minutes(5); // Invoke Lambda via SNS Topic const fn = new lambda.Function(this, 'Function', { @@ -55,7 +51,7 @@ export class InstanceDrainHook extends cdk.Construct { runtime: lambda.Runtime.Python36, // Timeout: some extra margin for additional API calls made by the Lambda, // up to a maximum of 15 minutes. - timeout: Math.min(drainTimeSeconds + 10, 900), + timeout: cdk.Duration.seconds(Math.min(drainTime.toSeconds() + 10, 900)), environment: { CLUSTER: props.cluster.clusterName } @@ -66,7 +62,7 @@ export class InstanceDrainHook extends cdk.Construct { lifecycleTransition: autoscaling.LifecycleTransition.InstanceTerminating, defaultResult: autoscaling.DefaultResult.Continue, notificationTarget: new hooks.FunctionHook(fn), - heartbeatTimeoutSec: drainTimeSeconds, + heartbeatTimeout: drainTime, }); // FIXME: These should probably be restricted usefully in some way, but I don't exactly diff --git a/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts b/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts index b21e77d74ccc0..2c752ba0ad1ea 100644 --- a/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts +++ b/packages/@aws-cdk/aws-ecs/lib/log-drivers/aws-log-driver.ts @@ -34,7 +34,7 @@ export interface AwsLogDriverProps { * * @default - Logs never expire. */ - readonly logRetentionDays?: logs.RetentionDays; + readonly logRetention?: logs.RetentionDays; /** * This option defines a multiline start pattern in Python strftime format. @@ -73,12 +73,12 @@ export class AwsLogDriver extends LogDriver { constructor(scope: cdk.Construct, id: string, private readonly props: AwsLogDriverProps) { super(scope, id); - if (props.logGroup && props.logRetentionDays) { + if (props.logGroup && props.logRetention) { throw new Error('Cannot specify both `logGroup` and `logRetentionDays`.'); } this.logGroup = props.logGroup || new logs.LogGroup(this, 'LogGroup', { - retentionDays: props.logRetentionDays || Infinity, + retention: props.logRetention || Infinity, }); } diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts index 891d3592325bc..80228781b1665 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts @@ -1034,7 +1034,7 @@ export = { }, namespace: 'AWS/ECS', metricName: 'MemoryUtilization', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Average' }); diff --git a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts index 16950b85d80ef..cfe58d85319a9 100644 --- a/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/test/fargate/test.fargate-service.ts @@ -174,7 +174,7 @@ export = { new ecs.FargateService(stack, 'Svc', { cluster, taskDefinition, - healthCheckGracePeriodSeconds: 10 + healthCheckGracePeriod: cdk.Duration.seconds(10) }); // THEN @@ -493,7 +493,7 @@ export = { }, namespace: 'AWS/ECS', metricName: 'CPUUtilization', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Average' }); diff --git a/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts b/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts index 159205c50d31b..71a54e03301a5 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.aws-log-driver.ts @@ -12,7 +12,7 @@ export = { // WHEN const driver = new ecs.AwsLogDriver(stack, 'Log', { datetimeFormat: 'format', - logRetentionDays: logs.RetentionDays.OneMonth, + logRetention: logs.RetentionDays.OneMonth, multilinePattern: 'pattern', streamPrefix: 'hello' }); @@ -74,7 +74,7 @@ export = { // THEN test.throws(() => new ecs.AwsLogDriver(stack, 'Log', { logGroup, - logRetentionDays: logs.RetentionDays.FiveDays, + logRetention: logs.RetentionDays.FiveDays, streamPrefix: 'hello' }), /`logGroup`.*`logRetentionDays`/); diff --git a/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts b/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts index e0661b909e510..13c0e03d70bfa 100644 --- a/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/test/test.container-definition.ts @@ -372,9 +372,9 @@ export = { memoryLimitMiB: 1024, healthCheck: { command: [hcCommand], - intervalSeconds: 20, + interval: cdk.Duration.seconds(20), retries: 5, - startPeriod: 10 + startPeriod: cdk.Duration.seconds(10) } }); @@ -408,9 +408,9 @@ export = { memoryLimitMiB: 1024, healthCheck: { command: ["CMD-SHELL", hcCommand], - intervalSeconds: 20, + interval: cdk.Duration.seconds(20), retries: 5, - startPeriod: 10 + startPeriod: cdk.Duration.seconds(10) } }); @@ -444,9 +444,9 @@ export = { memoryLimitMiB: 1024, healthCheck: { command: ["CMD", hcCommand], - intervalSeconds: 20, + interval: cdk.Duration.seconds(20), retries: 5, - startPeriod: 10 + startPeriod: cdk.Duration.seconds(10) } }); diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts index fad3d826d81fc..ed969cfb1b243 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts @@ -1,7 +1,7 @@ import { AnyIPv4, Connections, IConnectable, IPortRange, ISecurityGroup, ISubnet, IVpc, SecurityGroup, TcpPort } from '@aws-cdk/aws-ec2'; -import { Construct, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, Lazy, Resource } from '@aws-cdk/cdk'; import { CfnLoadBalancer } from './elasticloadbalancing.generated'; /** @@ -96,16 +96,16 @@ export interface HealthCheck { /** * Number of seconds between health checks * - * @default 30 + * @default Duration.seconds(30) */ - readonly interval?: number; + readonly interval?: Duration; /** * Health check timeout * - * @default 5 + * @default Duration.seconds(5) */ - readonly timeout?: number; + readonly timeout?: Duration; } /** @@ -411,9 +411,9 @@ function healthCheckToJSON(healthCheck: HealthCheck): CfnLoadBalancer.HealthChec return { healthyThreshold: ifUndefined(healthCheck.healthyThreshold, 2).toString(), - interval: ifUndefined(healthCheck.interval, 30).toString(), + interval: (healthCheck.interval || Duration.seconds(30)).toSeconds().toString(), target, - timeout: ifUndefined(healthCheck.timeout, 5).toString(), + timeout: (healthCheck.timeout || Duration.seconds(5)).toSeconds().toString(), unhealthyThreshold: ifUndefined(healthCheck.unhealthyThreshold, 5).toString(), }; } diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/test/test.loadbalancer.ts b/packages/@aws-cdk/aws-elasticloadbalancing/test/test.loadbalancer.ts index 96c94a973def7..4f649756ba638 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/test/test.loadbalancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancing/test/test.loadbalancer.ts @@ -1,6 +1,6 @@ import { expect, haveResource } from '@aws-cdk/assert'; import { CidrIPv4, Connections, Vpc } from '@aws-cdk/aws-ec2'; -import { Stack } from '@aws-cdk/cdk'; +import { Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { ILoadBalancerTarget, LoadBalancer, LoadBalancingProtocol } from '../lib'; @@ -40,7 +40,7 @@ export = { new LoadBalancer(stack, 'LB', { vpc, healthCheck: { - interval: 60, + interval: Duration.minutes(1), path: '/ping', protocol: LoadBalancingProtocol.Https, port: 443, @@ -68,7 +68,7 @@ export = { const elb = new LoadBalancer(stack, 'LB', { vpc, healthCheck: { - interval: 60, + interval: Duration.minutes(1), path: '/ping', protocol: LoadBalancingProtocol.Https, port: 443, diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts index adb38a833cffc..ed7c60a8d2c5b 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener.ts @@ -1,5 +1,5 @@ import ec2 = require('@aws-cdk/aws-ec2'); -import { Construct, IResource, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Lazy, Resource } from '@aws-cdk/cdk'; import { BaseListener } from '../shared/base-listener'; import { HealthCheck } from '../shared/base-target-group'; import { ApplicationProtocol, SslPolicy } from '../shared/enums'; @@ -190,12 +190,12 @@ export class ApplicationListener extends BaseListener implements IApplicationLis } const group = new ApplicationTargetGroup(this, id + 'Group', { - deregistrationDelaySec: props.deregistrationDelaySec, + deregistrationDelay: props.deregistrationDelay, healthCheck: props.healthCheck, port: props.port, protocol: props.protocol, - slowStartSec: props.slowStartSec, - stickinessCookieDurationSec: props.stickinessCookieDurationSec, + slowStart: props.slowStart, + stickinessCookieDuration: props.stickinessCookieDuration, targetGroupName: props.targetGroupName, targets: props.targets, vpc: this.loadBalancer.vpc, @@ -491,11 +491,11 @@ export interface AddApplicationTargetsProps extends AddRuleProps { * The time period during which the load balancer sends a newly registered * target a linearly increasing share of the traffic to the target group. * - * The range is 30–900 seconds (15 minutes). + * The range is 30-900 seconds (15 minutes). * * @default 0 */ - readonly slowStartSec?: number; + readonly slowStart?: Duration; /** * The stickiness cookie expiration period. @@ -505,9 +505,9 @@ export interface AddApplicationTargetsProps extends AddRuleProps { * After this period, the cookie is considered stale. The minimum value is * 1 second and the maximum value is 7 days (604800 seconds). * - * @default 86400 (1 day) + * @default Duration.days(1) */ - readonly stickinessCookieDurationSec?: number; + readonly stickinessCookieDuration?: Duration; /** * The targets to add to this target group. @@ -532,11 +532,11 @@ export interface AddApplicationTargetsProps extends AddRuleProps { /** * The amount of time for Elastic Load Balancing to wait before deregistering a target. * - * The range is 0–3600 seconds. + * The range is 0-3600 seconds. * - * @default 300 + * @default Duration.minutes(5) */ - readonly deregistrationDelaySec?: number; + readonly deregistrationDelay?: Duration; /** * Health check configuration diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts index 023dd5686c155..9531a07183f30 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-load-balancer.ts @@ -2,7 +2,7 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import s3 = require('@aws-cdk/aws-s3'); -import { Construct, Lazy, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Lazy, Resource, Stack } from '@aws-cdk/cdk'; import { BaseLoadBalancer, BaseLoadBalancerProps, ILoadBalancerV2 } from '../shared/base-load-balancer'; import { IpAddressType } from '../shared/enums'; import { ApplicationListener, BaseApplicationListenerProps } from './application-listener'; @@ -39,7 +39,7 @@ export interface ApplicationLoadBalancerProps extends BaseLoadBalancerProps { * * @default 60 */ - readonly idleTimeoutSecs?: number; + readonly idleTimeout?: Duration; } /** @@ -75,7 +75,7 @@ export class ApplicationLoadBalancer extends BaseLoadBalancer implements IApplic this.connections = new ec2.Connections({ securityGroups: [this.securityGroup] }); if (props.http2Enabled === false) { this.setAttribute('routing.http2.enabled', 'false'); } - if (props.idleTimeoutSecs !== undefined) { this.setAttribute('idle_timeout.timeout_seconds', props.idleTimeoutSecs.toString()); } + if (props.idleTimeout !== undefined) { this.setAttribute('idle_timeout.timeout_seconds', props.idleTimeout.toSeconds().toString()); } } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts index 8df988f15f5a8..0bc5be5b55a40 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts @@ -1,6 +1,6 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import ec2 = require('@aws-cdk/aws-ec2'); -import { Construct, IConstruct } from '@aws-cdk/cdk'; +import { Construct, Duration, IConstruct } from '@aws-cdk/cdk'; import { BaseTargetGroupProps, ITargetGroup, loadBalancerNameFromListenerArn, LoadBalancerTargetProps, TargetGroupBase, TargetGroupImportProps } from '../shared/base-target-group'; import { ApplicationProtocol } from '../shared/enums'; @@ -31,11 +31,11 @@ export interface ApplicationTargetGroupProps extends BaseTargetGroupProps { * The time period during which the load balancer sends a newly registered * target a linearly increasing share of the traffic to the target group. * - * The range is 30–900 seconds (15 minutes). + * The range is 30-900 seconds (15 minutes). * * @default 0 */ - readonly slowStartSec?: number; + readonly slowStart?: Duration; /** * The stickiness cookie expiration period. @@ -45,9 +45,9 @@ export interface ApplicationTargetGroupProps extends BaseTargetGroupProps { * After this period, the cookie is considered stale. The minimum value is * 1 second and the maximum value is 7 days (604800 seconds). * - * @default 86400 (1 day) + * @default Duration.days(1) */ - readonly stickinessCookieDurationSec?: number; + readonly stickinessCookieDuration?: Duration; /** * The targets to add to this target group. @@ -86,11 +86,11 @@ export class ApplicationTargetGroup extends TargetGroupBase implements IApplicat this.connectableMembers = []; this.listeners = []; - if (props.slowStartSec !== undefined) { - this.setAttribute('slow_start.duration_seconds', props.slowStartSec.toString()); + if (props.slowStart !== undefined) { + this.setAttribute('slow_start.duration_seconds', props.slowStart.toSeconds().toString()); } - if (props.stickinessCookieDurationSec !== undefined) { - this.enableCookieStickiness(props.stickinessCookieDurationSec); + if (props.stickinessCookieDuration !== undefined) { + this.enableCookieStickiness(props.stickinessCookieDuration); } this.addTarget(...(props.targets || [])); @@ -109,10 +109,10 @@ export class ApplicationTargetGroup extends TargetGroupBase implements IApplicat /** * Enable sticky routing via a cookie to members of this target group */ - public enableCookieStickiness(durationSec: number) { + public enableCookieStickiness(duration: Duration) { this.setAttribute('stickiness.enabled', 'true'); this.setAttribute('stickiness.type', 'lb_cookie'); - this.setAttribute('stickiness.lb_cookie.duration_seconds', durationSec.toString()); + this.setAttribute('stickiness.lb_cookie.duration_seconds', duration.toSeconds().toString()); } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts index b84c92ca8deaa..112616f85a8d3 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Resource } from '@aws-cdk/cdk'; import { BaseListener } from '../shared/base-listener'; import { HealthCheck } from '../shared/base-target-group'; import { Protocol, SslPolicy } from '../shared/enums'; @@ -140,7 +140,7 @@ export class NetworkListener extends BaseListener implements INetworkListener { } const group = new NetworkTargetGroup(this, id + 'Group', { - deregistrationDelaySec: props.deregistrationDelaySec, + deregistrationDelay: props.deregistrationDelay, healthCheck: props.healthCheck, port: props.port, proxyProtocolV2: props.proxyProtocolV2, @@ -202,9 +202,9 @@ export interface AddNetworkTargetsProps { * * The range is 0-3600 seconds. * - * @default 300 + * @default Duration.minutes(5) */ - readonly deregistrationDelaySec?: number; + readonly deregistrationDelay?: Duration; /** * Indicates whether Proxy Protocol version 2 is enabled. diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts index 930487f9bae5c..7b4cb5925ecea 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts @@ -31,7 +31,7 @@ export interface BaseTargetGroupProps { * * @default 300 */ - readonly deregistrationDelaySec?: number; + readonly deregistrationDelay?: cdk.Duration; /** * Health check configuration @@ -59,9 +59,9 @@ export interface HealthCheck { /** * The approximate number of seconds between health checks for an individual target. * - * @default 30 + * @default Duration.seconds(30) */ - readonly intervalSecs?: number; + readonly interval?: cdk.Duration; /** * The ping path destination where Elastic Load Balancing sends health check requests. @@ -90,13 +90,13 @@ export interface HealthCheck { /** * The amount of time, in seconds, during which no response from a target means a failed health check. * - * For Application Load Balancers, the range is 2–60 seconds and the + * For Application Load Balancers, the range is 2-60 seconds and the * default is 5 seconds. For Network Load Balancers, this is 10 seconds for * TCP and HTTPS health checks and 6 seconds for HTTP health checks. * - * @default 5 for ALBs, 10 or 6 for NLBs + * @default Duration.seconds(5) for ALBs, Duration.seconds(10) or Duration.seconds(6) for NLBs */ - readonly timeoutSeconds?: number; + readonly timeout?: cdk.Duration; /** * The number of consecutive health checks successes required before considering an unhealthy target healthy. @@ -204,8 +204,8 @@ export abstract class TargetGroupBase extends cdk.Construct implements ITargetGr constructor(scope: cdk.Construct, id: string, baseProps: BaseTargetGroupProps, additionalProps: any) { super(scope, id); - if (baseProps.deregistrationDelaySec !== undefined) { - this.setAttribute('deregistration_delay.timeout_seconds', baseProps.deregistrationDelaySec.toString()); + if (baseProps.deregistrationDelay !== undefined) { + this.setAttribute('deregistration_delay.timeout_seconds', baseProps.deregistrationDelay.toString()); } this.healthCheck = baseProps.healthCheck || {}; @@ -219,11 +219,15 @@ export abstract class TargetGroupBase extends cdk.Construct implements ITargetGr vpcId: baseProps.vpc.vpcId, // HEALTH CHECK - healthCheckIntervalSeconds: cdk.Lazy.numberValue({ produce: () => this.healthCheck && this.healthCheck.intervalSecs }), + healthCheckIntervalSeconds: cdk.Lazy.numberValue({ + produce: () => this.healthCheck && this.healthCheck.interval && this.healthCheck.interval.toSeconds() + }), healthCheckPath: cdk.Lazy.stringValue({ produce: () => this.healthCheck && this.healthCheck.path }), healthCheckPort: cdk.Lazy.stringValue({ produce: () => this.healthCheck && this.healthCheck.port }), healthCheckProtocol: cdk.Lazy.stringValue({ produce: () => this.healthCheck && this.healthCheck.protocol }), - healthCheckTimeoutSeconds: cdk.Lazy.numberValue({ produce: () => this.healthCheck && this.healthCheck.timeoutSeconds }), + healthCheckTimeoutSeconds: cdk.Lazy.numberValue({ + produce: () => this.healthCheck && this.healthCheck.timeout && this.healthCheck.timeout.toSeconds() + }), healthyThresholdCount: cdk.Lazy.numberValue({ produce: () => this.healthCheck && this.healthCheck.healthyThresholdCount }), unhealthyThresholdCount: cdk.Lazy.numberValue({ produce: () => this.healthCheck && this.healthCheck.unhealthyThresholdCount }), matcher: cdk.Lazy.anyValue({ produce: () => this.healthCheck && this.healthCheck.healthyHttpCodes !== undefined ? { diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts index 5e77a74014285..86a5bda1fe5c3 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.listener.ts @@ -292,7 +292,7 @@ export = { port: 80, targets: [new FakeSelfRegisteringTarget(stack, 'Target', vpc)] }); - group.enableCookieStickiness(3600); + group.enableCookieStickiness(cdk.Duration.hours(1)); // THEN expect(stack).to(haveResource('AWS::ElasticLoadBalancingV2::TargetGroup', { @@ -329,8 +329,8 @@ export = { }); group.configureHealthCheck({ unhealthyThresholdCount: 3, - timeoutSeconds: 3600, - intervalSecs: 30, + timeout: cdk.Duration.hours(1), + interval: cdk.Duration.seconds(30), path: '/test', }); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts index 0b607002a2562..9d8310e584b3d 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/test.load-balancer.ts @@ -87,7 +87,7 @@ export = { vpc, deletionProtection: true, http2Enabled: false, - idleTimeoutSecs: 1000, + idleTimeout: cdk.Duration.seconds(1000), }); // THEN diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts index 854b06801f6dc..744ccbd62c910 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/nlb/test.listener.ts @@ -99,8 +99,8 @@ export = { targets: [new FakeSelfRegisteringTarget(stack, 'Target', vpc)] }); group.configureHealthCheck({ - timeoutSeconds: 3600, - intervalSecs: 30, + timeout: cdk.Duration.hours(1), + interval: cdk.Duration.seconds(30), path: '/test', }); diff --git a/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts b/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts index 5e29cd47b046c..840f9ae418162 100644 --- a/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts +++ b/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts @@ -11,7 +11,7 @@ test('State machine can be used as Event Rule target', () => { scheduleExpression: 'rate(1 minute)' }); const stateMachine = new sfn.StateMachine(stack, 'SM', { - definition: new sfn.Wait(stack, 'Hello', { duration: sfn.WaitDuration.seconds(10) }) + definition: new sfn.Wait(stack, 'Hello', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)) }) }); // WHEN diff --git a/packages/@aws-cdk/aws-iam/lib/role.ts b/packages/@aws-cdk/aws-iam/lib/role.ts index 3f59ae06fcc77..53b19ac53fd8a 100644 --- a/packages/@aws-cdk/aws-iam/lib/role.ts +++ b/packages/@aws-cdk/aws-iam/lib/role.ts @@ -1,4 +1,4 @@ -import { Construct, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, PhysicalName, Resource, ResourceIdentifiers, Stack, toSeconds } from '@aws-cdk/cdk'; import { Grant } from './grant'; import { CfnRole } from './iam.generated'; import { IIdentity } from './identity-base'; @@ -71,9 +71,8 @@ export interface RoleProps { readonly roleName?: PhysicalName; /** - * The maximum session duration (in seconds) that you want to set for the - * specified role. This setting can have a value from 1 hour (3600sec) to - * 12 (43200sec) hours. + * The maximum session duration that you want to set for the specified role. + * This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours. * * Anyone who assumes the role from the AWS CLI or API can use the * DurationSeconds API parameter or the duration-seconds CLI parameter to @@ -88,9 +87,9 @@ export interface RoleProps { * * @link https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html * - * @default 3600 (1 hour) + * @default Duration.hours(1) */ - readonly maxSessionDurationSec?: number; + readonly maxSessionDuration?: Duration; } /** @@ -197,7 +196,8 @@ export class Role extends Resource implements IRole { this.assumeRolePolicy = createAssumeRolePolicy(props.assumedBy, props.externalId); this.managedPolicyArns = props.managedPolicyArns || [ ]; - validateMaxSessionDuration(props.maxSessionDurationSec); + const maxSessionDuration = toSeconds(props.maxSessionDuration); + validateMaxSessionDuration(maxSessionDuration); const role = new CfnRole(this, 'Resource', { assumeRolePolicyDocument: this.assumeRolePolicy as any, @@ -205,7 +205,7 @@ export class Role extends Resource implements IRole { policies: _flatten(props.inlinePolicies), path: props.path, roleName: this.physicalName.value, - maxSessionDuration: props.maxSessionDurationSec, + maxSessionDuration, }); this.roleId = role.roleId; diff --git a/packages/@aws-cdk/aws-iam/test/test.role.ts b/packages/@aws-cdk/aws-iam/test/test.role.ts index c3783e814ba4d..30618533ce9d9 100644 --- a/packages/@aws-cdk/aws-iam/test/test.role.ts +++ b/packages/@aws-cdk/aws-iam/test/test.role.ts @@ -1,5 +1,5 @@ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { ArnPrincipal, CompositePrincipal, FederatedPrincipal, PolicyStatement, Role, ServicePrincipal, User } from '../lib'; @@ -194,7 +194,7 @@ export = { 'can be used to specify the maximum session duration for assuming the role'(test: Test) { const stack = new Stack(); - new Role(stack, 'MyRole', { maxSessionDurationSec: 3700, assumedBy: new ServicePrincipal('sns.amazonaws.com') }); + new Role(stack, 'MyRole', { maxSessionDuration: Duration.seconds(3700), assumedBy: new ServicePrincipal('sns.amazonaws.com') }); expect(stack).to(haveResource('AWS::IAM::Role', { MaxSessionDuration: 3700 @@ -208,13 +208,13 @@ export = { const assumedBy = new ServicePrincipal('bla'); - new Role(stack, 'MyRole1', { assumedBy, maxSessionDurationSec: 3600 }); - new Role(stack, 'MyRole2', { assumedBy, maxSessionDurationSec: 43200 }); + new Role(stack, 'MyRole1', { assumedBy, maxSessionDuration: Duration.hours(1) }); + new Role(stack, 'MyRole2', { assumedBy, maxSessionDuration: Duration.hours(12) }); const expected = (val: any) => `maxSessionDuration is set to ${val}, but must be >= 3600sec (1hr) and <= 43200sec (12hrs)`; - test.throws(() => new Role(stack, 'MyRole3', { assumedBy, maxSessionDurationSec: 60 }), expected(60)); - test.throws(() => new Role(stack, 'MyRole4', { assumedBy, maxSessionDurationSec: 3599 }), expected(3599)); - test.throws(() => new Role(stack, 'MyRole5', { assumedBy, maxSessionDurationSec: 43201 }), expected(43201)); + test.throws(() => new Role(stack, 'MyRole3', { assumedBy, maxSessionDuration: Duration.minutes(1) }), expected(60)); + test.throws(() => new Role(stack, 'MyRole4', { assumedBy, maxSessionDuration: Duration.seconds(3599) }), expected(3599)); + test.throws(() => new Role(stack, 'MyRole5', { assumedBy, maxSessionDuration: Duration.seconds(43201) }), expected(43201)); test.done(); } diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 5151cf57eb958..b66e4a1920adb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -3,7 +3,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import logs = require('@aws-cdk/aws-logs'); import sqs = require('@aws-cdk/aws-sqs'); -import { Construct, Fn, Lazy, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Fn, Lazy, Stack, toSeconds } from '@aws-cdk/cdk'; import { Code } from './code'; import { IEventSource } from './event-source'; import { FunctionAttributes, FunctionBase, IFunction } from './function-base'; @@ -64,9 +64,9 @@ export interface FunctionProps { * the function. Because the execution time affects cost, set this value * based on the function's expected execution time. * - * @default 3 + * @default Duration.seconds(3) */ - readonly timeout?: number; + readonly timeout?: Duration; /** * Key-value pairs that Lambda caches and makes available for your Lambda @@ -219,7 +219,7 @@ export interface FunctionProps { * * @default - Logs never expire. */ - readonly logRetentionDays?: logs.RetentionDays; + readonly logRetention?: logs.RetentionDays; } /** @@ -427,7 +427,7 @@ export class Function extends FunctionBase { code: Lazy.anyValue({ produce: () => props.code._toJSON(resource) }), layers: Lazy.listValue({ produce: () => this.layers.map(layer => layer.layerVersionArn) }, { omitEmpty: true }), handler: props.handler, - timeout: props.timeout, + timeout: toSeconds(props.timeout), runtime: props.runtime.name, role: this.role.roleArn, environment: Lazy.anyValue({ produce: () => this.renderEnvironment() }), @@ -457,10 +457,10 @@ export class Function extends FunctionBase { } // Log retention - if (props.logRetentionDays) { + if (props.logRetention) { new LogRetention(this, 'LogRetention', { logGroupName: `/aws/lambda/${this.functionName}`, - retentionDays: props.logRetentionDays + retention: props.logRetention }); } } @@ -609,7 +609,7 @@ export class Function extends FunctionBase { } const deadLetterQueue = props.deadLetterQueue || new sqs.Queue(this, 'DeadLetterQueue', { - retentionPeriodSec: 1209600 + retentionPeriod: Duration.days(14) }); this.addToRolePolicy(new iam.PolicyStatement() diff --git a/packages/@aws-cdk/aws-lambda/lib/log-retention.ts b/packages/@aws-cdk/aws-lambda/lib/log-retention.ts index 1196f04d38012..95dd25ff16cce 100644 --- a/packages/@aws-cdk/aws-lambda/lib/log-retention.ts +++ b/packages/@aws-cdk/aws-lambda/lib/log-retention.ts @@ -18,7 +18,7 @@ export interface LogRetentionProps { /** * The number of days log events are kept in CloudWatch Logs. */ - readonly retentionDays: logs.RetentionDays; + readonly retention: logs.RetentionDays; } /** @@ -55,7 +55,7 @@ export class LogRetention extends cdk.Construct { properties: { ServiceToken: provider.functionArn, LogGroupName: props.logGroupName, - RetentionInDays: props.retentionDays === Infinity ? undefined : props.retentionDays + RetentionInDays: props.retention === Infinity ? undefined : props.retention } }); } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.log-retention.ts b/packages/@aws-cdk/aws-lambda/test/integ.log-retention.ts index cc52b278fb857..1be7666cded7e 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.log-retention.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.log-retention.ts @@ -10,21 +10,21 @@ new lambda.Function(stack, 'OneWeek', { code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'), handler: 'index.handler', runtime: lambda.Runtime.NodeJS810, - logRetentionDays: logs.RetentionDays.OneWeek + logRetention: logs.RetentionDays.OneWeek }); new lambda.Function(stack, 'OneMonth', { code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'), handler: 'index.handler', runtime: lambda.Runtime.NodeJS810, - logRetentionDays: logs.RetentionDays.OneMonth + logRetention: logs.RetentionDays.OneMonth }); new lambda.Function(stack, 'OneYear', { code: new lambda.InlineCode('exports.handler = (event) => console.log(JSON.stringify(event));'), handler: 'index.handler', runtime: lambda.Runtime.NodeJS810, - logRetentionDays: logs.RetentionDays.OneYear + logRetention: logs.RetentionDays.OneYear }); app.synth(); diff --git a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts index f491ff4eb101c..918b5f6fafff7 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.lambda.ts @@ -583,7 +583,7 @@ export = { const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { queueName: 'MyLambda_DLQ', - retentionPeriodSec: 1209600 + retentionPeriod: cdk.Duration.days(14) }); new lambda.Function(stack, 'MyLambda', { @@ -692,7 +692,7 @@ export = { const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { queueName: 'MyLambda_DLQ', - retentionPeriodSec: 1209600 + retentionPeriod: cdk.Duration.days(14) }); new lambda.Function(stack, 'MyLambda', { @@ -802,7 +802,7 @@ export = { const dlQueue = new sqs.Queue(stack, 'DeadLetterQueue', { queueName: 'MyLambda_DLQ', - retentionPeriodSec: 1209600 + retentionPeriod: cdk.Duration.days(14) }); test.throws(() => new lambda.Function(stack, 'MyLambda', { @@ -1088,7 +1088,7 @@ export = { dimensions: { FunctionName: { Ref: 'Function76856677' }}, namespace: 'AWS/Lambda', metricName: 'Errors', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Sum', }); @@ -1323,7 +1323,7 @@ export = { code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.NodeJS, - logRetentionDays: logs.RetentionDays.OneMonth + logRetention: logs.RetentionDays.OneMonth }); // THEN diff --git a/packages/@aws-cdk/aws-lambda/test/test.log-retention.ts b/packages/@aws-cdk/aws-lambda/test/test.log-retention.ts index be223b42148d7..c5409df0527f5 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.log-retention.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.log-retention.ts @@ -14,7 +14,7 @@ export = { // WHEN new lambda.LogRetention(stack, 'MyLambda', { logGroupName: 'group', - retentionDays: logs.RetentionDays.OneMonth + retention: logs.RetentionDays.OneMonth }); // THEN diff --git a/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts index 5a49886d7658b..74b02f61e6890 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.singleton-lambda.ts @@ -15,7 +15,7 @@ export = { code: new lambda.InlineCode('def hello(): pass'), runtime: lambda.Runtime.Python27, handler: 'index.hello', - timeout: 300, + timeout: cdk.Duration.minutes(5), }); } diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index 6881d573ecc0f..834487e094cb6 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -284,9 +284,9 @@ export interface LogGroupProps { * * To retain all logs, set this value to Infinity. * - * @default 731 days (2 years) + * @default RetentionDays.TwoYears */ - readonly retentionDays?: RetentionDays; + readonly retention?: RetentionDays; /** * Retain the log group if the stack or containing construct ceases to exist @@ -330,7 +330,7 @@ export class LogGroup extends LogGroupBase { constructor(scope: Construct, id: string, props: LogGroupProps = {}) { super(scope, id); - let retentionInDays = props.retentionDays; + let retentionInDays = props.retention; if (retentionInDays === undefined) { retentionInDays = RetentionDays.TwoYears; } if (retentionInDays === Infinity) { retentionInDays = undefined; } diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 660e812aa68bc..f1db7cef9885e 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -89,4 +89,4 @@ ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-logs/test/example.retention.lit.ts b/packages/@aws-cdk/aws-logs/test/example.retention.lit.ts index ed0fd084e18d4..4ba54c254178f 100644 --- a/packages/@aws-cdk/aws-logs/test/example.retention.lit.ts +++ b/packages/@aws-cdk/aws-logs/test/example.retention.lit.ts @@ -7,7 +7,7 @@ function shortLogGroup() { /// !show // Configure log group for short retention const logGroup = new LogGroup(stack, 'LogGroup', { - retentionDays: RetentionDays.OneWeek + retention: RetentionDays.OneWeek }); /// !hide return logGroup; @@ -17,7 +17,7 @@ function infiniteLogGroup() { /// !show // Configure log group for infinite retention const logGroup = new LogGroup(stack, 'LogGroup', { - retentionDays: Infinity + retention: Infinity }); /// !hide return logGroup; diff --git a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts index 16d13706284d0..0c08cdfd16a57 100644 --- a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts +++ b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts @@ -11,7 +11,7 @@ export = { // WHEN new LogGroup(stack, 'LogGroup', { - retentionDays: RetentionDays.OneWeek + retention: RetentionDays.OneWeek }); // THEN @@ -43,7 +43,7 @@ export = { // WHEN new LogGroup(stack, 'LogGroup', { - retentionDays: Infinity + retention: Infinity }); // THEN @@ -65,7 +65,7 @@ export = { // WHEN new LogGroup(stack, 'LogGroup', { - retentionDays: Infinity, + retention: Infinity, retainLogGroup: false }); diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 082a14efa92fc..c50ae5e43045b 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -312,7 +312,7 @@ export class DatabaseCluster extends DatabaseClusterBase { : (props.masterUser.password ? props.masterUser.password.toString() : undefined), - backupRetentionPeriod: props.backup && props.backup.retentionDays, + backupRetentionPeriod: props.backup && props.backup.retention && props.backup.retention.toDays(), preferredBackupWindow: props.backup && props.backup.preferredWindow, preferredMaintenanceWindow: props.preferredMaintenanceWindow, databaseName: props.defaultDatabaseName, diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index 3ae8dd578f85e..4a3c063ee0b45 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -5,7 +5,7 @@ import kms = require('@aws-cdk/aws-kms'); import lambda = require('@aws-cdk/aws-lambda'); import logs = require('@aws-cdk/aws-logs'); import secretsmanager = require('@aws-cdk/aws-secretsmanager'); -import { Construct, DeletionPolicy, IResource, Resource, SecretValue, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, DeletionPolicy, Duration, IResource, Resource, SecretValue, Stack, Token, toSeconds } from '@aws-cdk/cdk'; import { DatabaseSecret } from './database-secret'; import { Endpoint } from './endpoint'; import { IOptionGroup} from './option-group'; @@ -225,7 +225,7 @@ export enum StorageType { /** * The retention period for Performance Insight. */ -export enum PerformanceInsightRetentionPeriod { +export enum PerformanceInsightRetention { /** * Default retention period of 7 days. */ @@ -333,7 +333,7 @@ export interface DatabaseInstanceNewProps { * * @default 1 day */ - readonly backupRetentionPeriod?: number; + readonly backupRetention?: Duration; /** * The daily time range during which automated backups are performed. @@ -372,7 +372,7 @@ export interface DatabaseInstanceNewProps { * * @default no enhanced monitoring */ - readonly monitoringInterval?: number; + readonly monitoringInterval?: Duration; /** * Whether to enable Performance Insights for the DB instance. @@ -386,7 +386,7 @@ export interface DatabaseInstanceNewProps { * * @default 7 days */ - readonly performanceInsightRetentionPeriod?: PerformanceInsightRetentionPeriod; + readonly performanceInsightRetention?: PerformanceInsightRetention; /** * The AWS KMS key for encryption of Performance Insights data. @@ -507,7 +507,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData this.newCfnProps = { autoMinorVersionUpgrade: props.autoMinorVersionUpgrade, availabilityZone: props.multiAz ? undefined : props.availabilityZone, - backupRetentionPeriod: props.backupRetentionPeriod ? props.backupRetentionPeriod.toString() : undefined, + backupRetentionPeriod: props.backupRetention ? props.backupRetention.toDays().toString() : undefined, copyTagsToSnapshot: props.copyTagsToSnapshot !== undefined ? props.copyTagsToSnapshot : true, dbInstanceClass: `db.${props.instanceClass}`, dbInstanceIdentifier: props.instanceIdentifier, @@ -518,7 +518,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData enableIamDatabaseAuthentication: props.iamAuthentication, enablePerformanceInsights: props.enablePerformanceInsights, iops, - monitoringInterval: props.monitoringInterval, + monitoringInterval: toSeconds(props.monitoringInterval), monitoringRoleArn: monitoringRole && monitoringRole.roleArn, multiAz: props.multiAz, optionGroupName: props.optionGroup && props.optionGroup.optionGroupName, @@ -526,7 +526,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData ? props.performanceInsightKmsKey && props.performanceInsightKmsKey.keyArn : undefined, performanceInsightsRetentionPeriod: props.enablePerformanceInsights - ? (props.performanceInsightRetentionPeriod || PerformanceInsightRetentionPeriod.Default) + ? (props.performanceInsightRetention || PerformanceInsightRetention.Default) : undefined, port: props.port ? props.port.toString() : undefined, preferredBackupWindow: props.preferredBackupWindow, @@ -543,7 +543,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData for (const log of this.cloudwatchLogsExports) { new lambda.LogRetention(this, `LogRetention${log}`, { logGroupName: `/aws/rds/instance/${this.instanceIdentifier}/${log}`, - retentionDays: this.cloudwatchLogsRetention + retention: this.cloudwatchLogsRetention }); } } diff --git a/packages/@aws-cdk/aws-rds/lib/props.ts b/packages/@aws-cdk/aws-rds/lib/props.ts index cde179018c92d..ff26825559cb2 100644 --- a/packages/@aws-cdk/aws-rds/lib/props.ts +++ b/packages/@aws-cdk/aws-rds/lib/props.ts @@ -1,6 +1,6 @@ import ec2 = require('@aws-cdk/aws-ec2'); import kms = require('@aws-cdk/aws-kms'); -import { SecretValue } from '@aws-cdk/cdk'; +import { Duration, SecretValue } from '@aws-cdk/cdk'; import { IParameterGroup } from './parameter-group'; import { SecretRotationApplication } from './secret-rotation'; @@ -80,7 +80,7 @@ export interface BackupProps { /** * How many days to retain the backup */ - readonly retentionDays: number; + readonly retention: Duration; /** * A daily time range in 24-hours UTC format in which backups preferably execute. diff --git a/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts b/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts index b17ce3903cbb6..5bf663f6002f0 100644 --- a/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts +++ b/packages/@aws-cdk/aws-rds/lib/secret-rotation.ts @@ -2,7 +2,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import lambda = require('@aws-cdk/aws-lambda'); import serverless = require('@aws-cdk/aws-sam'); import secretsmanager = require('@aws-cdk/aws-secretsmanager'); -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Stack } from '@aws-cdk/cdk'; /** * A secret rotation serverless application. @@ -40,9 +40,9 @@ export interface SecretRotationOptions { * Specifies the number of days after the previous rotation before * Secrets Manager triggers the next automatic rotation. * - * @default 30 days + * @default Duration.days(30) */ - readonly automaticallyAfterDays?: number; + readonly automaticallyAfter?: Duration; } /** @@ -139,7 +139,7 @@ export class SecretRotation extends Construct { const rotationSchedule = props.secret.addRotationSchedule('RotationSchedule', { rotationLambda, - automaticallyAfterDays: props.automaticallyAfterDays + automaticallyAfter: props.automaticallyAfter }); rotationSchedule.node.addDependency(permission); // Cannot rotate without permission } diff --git a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts index 5229eb8cf3b41..92a78aa5edc14 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.instance.lit.ts @@ -53,8 +53,8 @@ class DatabaseInstanceStack extends cdk.Stack { vpc, databaseName: 'ORCL', storageEncrypted: true, - backupRetentionPeriod: 7, - monitoringInterval: 60, + backupRetention: cdk.Duration.days(7), + monitoringInterval: cdk.Duration.seconds(60), enablePerformanceInsights: true, cloudwatchLogsExports: [ 'trace', diff --git a/packages/@aws-cdk/aws-rds/test/test.instance.ts b/packages/@aws-cdk/aws-rds/test/test.instance.ts index 441fa691e6151..a65a8b1c601a6 100644 --- a/packages/@aws-cdk/aws-rds/test/test.instance.ts +++ b/packages/@aws-cdk/aws-rds/test/test.instance.ts @@ -24,8 +24,8 @@ export = { vpc, databaseName: 'ORCL', storageEncrypted: true, - backupRetentionPeriod: 7, - monitoringInterval: 60, + backupRetention: cdk.Duration.days(7), + monitoringInterval: cdk.Duration.seconds(60), enablePerformanceInsights: true, cloudwatchLogsExports: [ 'trace', @@ -39,74 +39,70 @@ export = { // THEN expect(stack).to(haveResource('AWS::RDS::DBInstance', { - Properties: { - DBInstanceClass: 'db.t2.medium', - AllocatedStorage: '100', - AutoMinorVersionUpgrade: false, - BackupRetentionPeriod: '7', - CopyTagsToSnapshot: true, - DBName: 'ORCL', - DBSubnetGroupName: { - Ref: 'InstanceSubnetGroupF2CBA54F' - }, - DeletionProtection: true, - EnableCloudwatchLogsExports: [ - 'trace', - 'audit', - 'alert', - 'listener' - ], - EnablePerformanceInsights: true, - Engine: 'oracle-se1', - Iops: 1000, - LicenseModel: 'bring-your-own-license', - MasterUsername: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'InstanceSecret478E0A47' - }, - ':SecretString:username::}}' - ] - ] - }, - MasterUserPassword: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'InstanceSecret478E0A47' - }, - ':SecretString:password::}}' - ] + DBInstanceClass: 'db.t2.medium', + AllocatedStorage: '100', + AutoMinorVersionUpgrade: false, + BackupRetentionPeriod: '7', + CopyTagsToSnapshot: true, + DBName: 'ORCL', + DBSubnetGroupName: { + Ref: 'InstanceSubnetGroupF2CBA54F' + }, + DeletionProtection: true, + EnableCloudwatchLogsExports: [ + 'trace', + 'audit', + 'alert', + 'listener' + ], + EnablePerformanceInsights: true, + Engine: 'oracle-se1', + Iops: 1000, + LicenseModel: 'bring-your-own-license', + MasterUsername: { + 'Fn::Join': [ + '', + [ + '{{resolve:secretsmanager:', + { + Ref: 'InstanceSecret478E0A47' + }, + ':SecretString:username::}}' ] - }, - MonitoringInterval: 60, - MonitoringRoleArn: { - 'Fn::GetAtt': [ - 'InstanceMonitoringRole3E2B4286', - 'Arn' + ] + }, + MasterUserPassword: { + 'Fn::Join': [ + '', + [ + '{{resolve:secretsmanager:', + { + Ref: 'InstanceSecret478E0A47' + }, + ':SecretString:password::}}' ] - }, - MultiAZ: true, - PerformanceInsightsRetentionPeriod: 7, - StorageEncrypted: true, - StorageType: 'io1', - VPCSecurityGroups: [ - { - 'Fn::GetAtt': [ - 'InstanceSecurityGroupB4E5FA83', - 'GroupId' - ] - } ] }, - DeletionPolicy: 'Retain', - UpdateReplacePolicy: 'Retain' - }, ResourcePart.CompleteDefinition)); + MonitoringInterval: 60, + MonitoringRoleArn: { + 'Fn::GetAtt': [ + 'InstanceMonitoringRole3E2B4286', + 'Arn' + ] + }, + MultiAZ: true, + PerformanceInsightsRetentionPeriod: 7, + StorageEncrypted: true, + StorageType: 'io1', + VPCSecurityGroups: [ + { + 'Fn::GetAtt': [ + 'InstanceSecurityGroupB4E5FA83', + 'GroupId' + ] + } + ] + })); expect(stack).to(haveResource('AWS::RDS::DBInstance', { DeletionPolicy: 'Retain', @@ -371,7 +367,7 @@ export = { dimensions: { DBInstanceIdentifier: { Ref: 'InstanceC1063A87' } }, namespace: 'AWS/RDS', metricName: 'CPUUtilization', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Average' }); diff --git a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts index fe3bf4b13a34e..bdb4099605ae6 100644 --- a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts +++ b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts @@ -1,5 +1,5 @@ import ec2 = require('@aws-cdk/aws-ec2'); -import { Construct, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, Lazy, Resource } from '@aws-cdk/cdk'; import { HostedZoneAttributes, IHostedZone } from './hosted-zone-ref'; import { CaaAmazonRecord, ZoneDelegationRecord } from './record-set'; import { CfnHostedZone } from './route53.generated'; @@ -184,7 +184,7 @@ export interface ZoneDelegationOptions { * * @default 172800 */ - readonly ttl?: number; + readonly ttl?: Duration; } export interface PrivateHostedZoneProps extends CommonHostedZoneProps { diff --git a/packages/@aws-cdk/aws-route53/lib/record-set.ts b/packages/@aws-cdk/aws-route53/lib/record-set.ts index 1d178c6abc69d..8bc4a272233a1 100644 --- a/packages/@aws-cdk/aws-route53/lib/record-set.ts +++ b/packages/@aws-cdk/aws-route53/lib/record-set.ts @@ -1,4 +1,4 @@ -import { Construct, IResource, Resource, Token } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Resource, Token, toSeconds } from '@aws-cdk/cdk'; import { IAliasRecordTarget } from './alias-record-target'; import { IHostedZone } from './hosted-zone-ref'; import { CfnRecordSet } from './route53.generated'; @@ -49,11 +49,11 @@ export interface RecordSetOptions { readonly recordName?: string; /** - * The resource record cache time to live (TTL) in seconds. + * The resource record cache time to live (TTL). * - * @default 1800 seconds + * @default Duration.minutes(30) */ - readonly ttl?: number; + readonly ttl?: Duration; /** * A comment to add on the record. @@ -110,7 +110,7 @@ export class RecordSet extends Resource implements IRecordSet { constructor(scope: Construct, id: string, props: RecordSetProps) { super(scope, id); - const ttl = props.target.aliasTarget ? undefined : (props.ttl || 1800).toString(); + const ttl = props.target.aliasTarget ? undefined : (toSeconds(props.ttl) || 1800).toString(); const recordSet = new CfnRecordSet(this, 'Resource', { hostedZoneId: props.zone.hostedZoneId, @@ -446,7 +446,7 @@ export class ZoneDelegationRecord extends RecordSet { ? props.nameServers // Can't map a string-array token! : props.nameServers.map(ns => (Token.isUnresolved(ns) || ns.endsWith('.')) ? ns : `${ns}.`) ), - ttl: props.ttl || 172_800 + ttl: props.ttl || Duration.days(2) }); } } diff --git a/packages/@aws-cdk/aws-route53/test/integ.route53.ts b/packages/@aws-cdk/aws-route53/test/integ.route53.ts index 544c84e09b80c..f50521f546257 100644 --- a/packages/@aws-cdk/aws-route53/test/integ.route53.ts +++ b/packages/@aws-cdk/aws-route53/test/integ.route53.ts @@ -27,7 +27,7 @@ new TxtRecord(privateZone, 'TXT', { 'Bar!', 'Baz?' ], - ttl: 60 + ttl: cdk.Duration.minutes(1) }); new CnameRecord(stack, 'CNAME', { diff --git a/packages/@aws-cdk/aws-route53/test/test.record-set.ts b/packages/@aws-cdk/aws-route53/test/test.record-set.ts index 8dca376245ab5..e455961379c72 100644 --- a/packages/@aws-cdk/aws-route53/test/test.record-set.ts +++ b/packages/@aws-cdk/aws-route53/test/test.record-set.ts @@ -1,5 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import route53 = require('../lib'); @@ -49,7 +49,7 @@ export = { recordName: 'aa', recordType: route53.RecordType.CNAME, target: route53.RecordTarget.fromValues('bbb'), - ttl: 6077 + ttl: Duration.seconds(6077) }); // THEN diff --git a/packages/@aws-cdk/aws-route53/test/test.route53.ts b/packages/@aws-cdk/aws-route53/test/test.route53.ts index e5cac2f6a1879..1eb06a4e82556 100644 --- a/packages/@aws-cdk/aws-route53/test/test.route53.ts +++ b/packages/@aws-cdk/aws-route53/test/test.route53.ts @@ -182,7 +182,7 @@ export = { const delegate = new PublicHostedZone(stack, 'SubZone', { zoneName: 'sub.top.test' }); // WHEN - zone.addDelegation(delegate, { ttl: 1337 }); + zone.addDelegation(delegate, { ttl: cdk.Duration.seconds(1337) }); // THEN expect(stack).to(haveResource('AWS::Route53::RecordSet', { diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index 77c427abfb8a3..833925ec9c6b7 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -49,7 +49,7 @@ export class BucketDeployment extends cdk.Construct { runtime: lambda.Runtime.Python36, handler: 'index.handler', lambdaPurpose: 'Custom::CDKBucketDeployment', - timeout: 15 * 60 + timeout: cdk.Duration.minutes(15) }); const source = props.source.bind(this); diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index b73d8725f32e9..6af7df69bae5c 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -875,7 +875,7 @@ export class Bucket extends BucketBase { * @param rule The rule to add */ public addLifecycleRule(rule: LifecycleRule) { - if ((rule.noncurrentVersionExpirationInDays !== undefined + if ((rule.noncurrentVersionExpiration !== undefined || (rule.noncurrentVersionTransitions && rule.noncurrentVersionTransitions.length > 0)) && !this.versioned) { throw new Error("Cannot use 'noncurrent' rules on a nonversioned bucket"); @@ -1061,17 +1061,24 @@ export class Bucket extends BucketBase { function parseLifecycleRule(rule: LifecycleRule): CfnBucket.RuleProperty { const enabled = rule.enabled !== undefined ? rule.enabled : true; - const x = { + const x: CfnBucket.RuleProperty = { // tslint:disable-next-line:max-line-length - abortIncompleteMultipartUpload: rule.abortIncompleteMultipartUploadAfterDays !== undefined ? { daysAfterInitiation: rule.abortIncompleteMultipartUploadAfterDays } : undefined, + abortIncompleteMultipartUpload: rule.abortIncompleteMultipartUploadAfter !== undefined ? { daysAfterInitiation: rule.abortIncompleteMultipartUploadAfter.toDays() } : undefined, expirationDate: rule.expirationDate, - expirationInDays: rule.expirationInDays, + expirationInDays: rule.expiration && rule.expiration.toDays(), id: rule.id, - noncurrentVersionExpirationInDays: rule.noncurrentVersionExpirationInDays, - noncurrentVersionTransitions: rule.noncurrentVersionTransitions, + noncurrentVersionExpirationInDays: rule.noncurrentVersionExpiration && rule.noncurrentVersionExpiration.toDays(), + noncurrentVersionTransitions: rule.noncurrentVersionTransitions && rule.noncurrentVersionTransitions.map(transition => ({ + storageClass: transition.storageClass, + transitionInDays: transition.transitionAfter.toDays(), + })), prefix: rule.prefix, status: enabled ? 'Enabled' : 'Disabled', - transitions: rule.transitions, + transitions: rule.transitions && rule.transitions.map(transition => ({ + storageClass: transition.storageClass, + transitionDate: transition.transitionDate, + transitionInDays: transition.transitionAfter && transition.transitionAfter.toDays(), + })), tagFilters: self.parseTagFilters(rule.tagFilters) }; diff --git a/packages/@aws-cdk/aws-s3/lib/rule.ts b/packages/@aws-cdk/aws-s3/lib/rule.ts index 186469dd0048a..f629568c6eca4 100644 --- a/packages/@aws-cdk/aws-s3/lib/rule.ts +++ b/packages/@aws-cdk/aws-s3/lib/rule.ts @@ -1,3 +1,5 @@ +import { Duration } from "@aws-cdk/cdk"; + /** * Declaration of a Life cycle rule */ @@ -24,7 +26,7 @@ export interface LifecycleRule { * * @default Incomplete uploads are never aborted */ - readonly abortIncompleteMultipartUploadAfterDays?: number; + readonly abortIncompleteMultipartUploadAfter?: Duration; /** * Indicates when objects are deleted from Amazon S3 and Amazon Glacier. @@ -48,7 +50,7 @@ export interface LifecycleRule { * * @default No expiration timeout */ - readonly expirationInDays?: number; + readonly expiration?: Duration; /** * Time between when a new version of the object is uploaded to the bucket and when old versions of the object expire. @@ -62,7 +64,7 @@ export interface LifecycleRule { * * @default No noncurrent version expiration */ - readonly noncurrentVersionExpirationInDays?: number; + readonly noncurrentVersionExpiration?: Duration; /** * One or more transition rules that specify when non-current objects transition to a specified storage class. @@ -123,7 +125,7 @@ export interface Transition { * * @default No transition count. */ - readonly transitionInDays?: number; + readonly transitionAfter?: Duration; } /** @@ -140,7 +142,7 @@ export interface NoncurrentVersionTransition { * * @default No transition count. */ - readonly transitionInDays: number; + readonly transitionAfter: Duration; } /** diff --git a/packages/@aws-cdk/aws-s3/test/test.rules.ts b/packages/@aws-cdk/aws-s3/test/test.rules.ts index 1724db63cd33f..d89064a556b1f 100644 --- a/packages/@aws-cdk/aws-s3/test/test.rules.ts +++ b/packages/@aws-cdk/aws-s3/test/test.rules.ts @@ -1,5 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/cdk'; +import { Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import { Bucket, StorageClass } from '../lib'; @@ -11,7 +11,7 @@ export = { // WHEN new Bucket(stack, 'Bucket', { lifecycleRules: [{ - expirationInDays: 30 + expiration: Duration.days(30) }] }); @@ -35,7 +35,7 @@ export = { // WHEN const bucket = new Bucket(stack, 'Bucket'); bucket.addLifecycleRule({ - expirationInDays: 30 + expiration: Duration.days(30) }); // THEN @@ -84,7 +84,7 @@ export = { lifecycleRules: [{ transitions: [{ storageClass: StorageClass.Glacier, - transitionInDays: 30 + transitionAfter: Duration.days(30) }] }] }); @@ -113,7 +113,7 @@ export = { test.throws(() => { new Bucket(stack, 'Bucket1', { lifecycleRules: [{ - noncurrentVersionExpirationInDays: 10 + noncurrentVersionExpiration: Duration.days(10) }] }); }); @@ -122,7 +122,7 @@ export = { new Bucket(stack, 'Bucket2', { versioned: true, lifecycleRules: [{ - noncurrentVersionExpirationInDays: 10 + noncurrentVersionExpiration: Duration.days(10) }] }); diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts index f261707ba1dd8..d342817a861fc 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts @@ -1,5 +1,5 @@ import lambda = require('@aws-cdk/aws-lambda'); -import { Construct, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, Resource } from '@aws-cdk/cdk'; import { ISecret } from './secret'; import { CfnRotationSchedule } from './secretsmanager.generated'; @@ -16,9 +16,9 @@ export interface RotationScheduleOptions { * Specifies the number of days after the previous rotation before * Secrets Manager triggers the next automatic rotation. * - * @default 30 + * @default Duration.days(30) */ - readonly automaticallyAfterDays?: number; + readonly automaticallyAfter?: Duration; } /** @@ -42,7 +42,7 @@ export class RotationSchedule extends Resource { secretId: props.secret.secretArn, rotationLambdaArn: props.rotationLambda.functionArn, rotationRules: { - automaticallyAfterDays: props.automaticallyAfterDays || 30 + automaticallyAfterDays: props.automaticallyAfter && props.automaticallyAfter.toDays() || 30 } }); } diff --git a/packages/@aws-cdk/aws-servicediscovery/lib/service.ts b/packages/@aws-cdk/aws-servicediscovery/lib/service.ts index 8188576b3929c..ae86ef72422b4 100644 --- a/packages/@aws-cdk/aws-servicediscovery/lib/service.ts +++ b/packages/@aws-cdk/aws-servicediscovery/lib/service.ts @@ -1,5 +1,5 @@ import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2'); -import { Construct, IResource, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Resource } from '@aws-cdk/cdk'; import { AliasTargetInstance } from './alias-target-instance'; import { CnameInstance, CnameInstanceBaseProps } from './cname-instance'; import { IInstance } from './instance'; @@ -98,9 +98,9 @@ export interface DnsServiceProps extends BaseServiceProps { * The amount of time, in seconds, that you want DNS resolvers to cache the settings for this * record. * - * @default 60 + * @default Duration.minutes(1) */ - readonly dnsTtlSec?: number; + readonly dnsTtl?: Duration; /** * The routing policy that you want to apply to all DNS records that AWS Cloud Map creates when you @@ -248,7 +248,7 @@ export class Service extends ServiceBase { const dnsConfig: CfnService.DnsConfigProperty | undefined = props.namespace.type === NamespaceType.Http ? undefined : { - dnsRecords: renderDnsRecords(dnsRecordType, props.dnsTtlSec), + dnsRecords: renderDnsRecords(dnsRecordType, props.dnsTtl), namespaceId: props.namespace.namespaceId, routingPolicy, }; @@ -324,8 +324,8 @@ export class Service extends ServiceBase { } } -function renderDnsRecords(dnsRecordType: DnsRecordType, dnsTtlSec?: number): CfnService.DnsRecordProperty[] { - const ttl = dnsTtlSec !== undefined ? dnsTtlSec : 60; +function renderDnsRecords(dnsRecordType: DnsRecordType, dnsTtl: Duration = Duration.minutes(1)): CfnService.DnsRecordProperty[] { + const ttl = dnsTtl.toSeconds(); if (dnsRecordType === DnsRecordType.A_AAAA) { return [{ diff --git a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-cname-record.lit.ts b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-cname-record.lit.ts index 58b0adba8b412..10d8f50be29cc 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-cname-record.lit.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-cname-record.lit.ts @@ -11,7 +11,7 @@ const namespace = new servicediscovery.PublicDnsNamespace(stack, 'Namespace', { const service = namespace.createService('Service', { name: 'foo', dnsRecordType: servicediscovery.DnsRecordType.CNAME, - dnsTtlSec: 30 + dnsTtl: cdk.Duration.seconds(30) }); service.registerCnameInstance('CnameInstance', { diff --git a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts index d785f0d960713..3aeecdc78abaf 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts @@ -15,7 +15,7 @@ const namespace = new servicediscovery.PrivateDnsNamespace(stack, 'Namespace', { const service = namespace.createService('Service', { dnsRecordType: servicediscovery.DnsRecordType.A_AAAA, - dnsTtlSec: 30, + dnsTtl: cdk.Duration.seconds(30), loadBalancer: true }); diff --git a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-public-dns-namespace.lit.ts b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-public-dns-namespace.lit.ts index 43a997827b6d4..b1cd88a0e483b 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-public-dns-namespace.lit.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-public-dns-namespace.lit.ts @@ -11,7 +11,7 @@ const namespace = new servicediscovery.PublicDnsNamespace(stack, 'Namespace', { const service = namespace.createService('Service', { name: 'foo', dnsRecordType: servicediscovery.DnsRecordType.A, - dnsTtlSec: 30, + dnsTtl: cdk.Duration.seconds(30), healthCheck: { type: servicediscovery.HealthCheckType.Https, resourcePath: '/healthcheck', diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index 4447c45d5d2b9..ea109055a41e7 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -212,7 +212,7 @@ export = { dimensions: {TopicName: { 'Fn::GetAtt': [ 'TopicBFC7AF6E', 'TopicName' ] }}, namespace: 'AWS/SNS', metricName: 'NumberOfMessagesPublished', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Sum' }); @@ -220,7 +220,7 @@ export = { dimensions: {TopicName: { 'Fn::GetAtt': [ 'TopicBFC7AF6E', 'TopicName' ] }}, namespace: 'AWS/SNS', metricName: 'PublishSize', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Average' }); diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index ad1a9bec47a12..374424264807f 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -1,5 +1,5 @@ import kms = require('@aws-cdk/aws-kms'); -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Stack, toSeconds } from '@aws-cdk/cdk'; import { IQueue, QueueAttributes, QueueBase } from './queue-base'; import { CfnQueue } from './sqs.generated'; import { validateProps } from './validate-props'; @@ -23,9 +23,9 @@ export interface QueueProps { * You can specify an integer value from 60 seconds (1 minute) to 1209600 * seconds (14 days). The default value is 345600 seconds (4 days). * - * @default 345600 seconds (4 days) + * @default Duration.days(4) */ - readonly retentionPeriodSec?: number; + readonly retentionPeriod?: Duration; /** * The time in seconds that the delivery of all messages in the queue is delayed. @@ -35,7 +35,7 @@ export interface QueueProps { * * @default 0 */ - readonly deliveryDelaySec?: number; + readonly deliveryDelay?: Duration; /** * The limit of how many bytes that a message can contain before Amazon SQS rejects it. @@ -57,7 +57,7 @@ export interface QueueProps { * * @default 0 */ - readonly receiveMessageWaitTimeSec?: number; + readonly receiveMessageWaitTime?: Duration; /** * Timeout of processing a single message. @@ -69,9 +69,9 @@ export interface QueueProps { * Values must be from 0 to 43200 seconds (12 hours). If you don't specify * a value, AWS CloudFormation uses the default value of 30 seconds. * - * @default 30 + * @default Duration.seconds(30) */ - readonly visibilityTimeoutSec?: number; + readonly visibilityTimeout?: Duration; /** * Send messages to this queue if they were unsuccessfully dequeued a number of times. @@ -111,9 +111,9 @@ export interface QueueProps { * The value must be an integer between 60 (1 minute) and 86,400 (24 * hours). The default is 300 (5 minutes). * - * @default 300 (5 minutes) + * @default Duration.minutes(5) */ - readonly dataKeyReuseSec?: number; + readonly dataKeyReuse?: Duration; /** * Whether this a first-in-first-out (FIFO) queue. @@ -248,11 +248,11 @@ export class Queue extends QueueBase { ...this.determineFifoProps(props), ...encryptionProps, redrivePolicy, - delaySeconds: props.deliveryDelaySec, + delaySeconds: toSeconds(props.deliveryDelay), maximumMessageSize: props.maxMessageSizeBytes, - messageRetentionPeriod: props.retentionPeriodSec, - receiveMessageWaitTimeSeconds: props.receiveMessageWaitTimeSec, - visibilityTimeout: props.visibilityTimeoutSec, + messageRetentionPeriod: toSeconds(props.retentionPeriod), + receiveMessageWaitTimeSeconds: toSeconds(props.receiveMessageWaitTime), + visibilityTimeout: toSeconds(props.visibilityTimeout), }); this.encryptionMasterKey = encryptionMasterKey; this.queueArn = queue.queueArn; @@ -277,7 +277,7 @@ export class Queue extends QueueBase { encryptionMasterKey: masterKey, encryptionProps: { kmsMasterKeyId: 'alias/aws/sqs', - kmsDataKeyReusePeriodSeconds: props.dataKeyReuseSec + kmsDataKeyReusePeriodSeconds: props.dataKeyReuse && props.dataKeyReuse.toSeconds() } }; } @@ -291,7 +291,7 @@ export class Queue extends QueueBase { encryptionMasterKey: masterKey, encryptionProps: { kmsMasterKeyId: masterKey.keyArn, - kmsDataKeyReusePeriodSeconds: props.dataKeyReuseSec + kmsDataKeyReusePeriodSeconds: props.dataKeyReuse && props.dataKeyReuse.toSeconds() } }; } diff --git a/packages/@aws-cdk/aws-sqs/lib/validate-props.ts b/packages/@aws-cdk/aws-sqs/lib/validate-props.ts index ecebbde44d42c..45bf28894e084 100644 --- a/packages/@aws-cdk/aws-sqs/lib/validate-props.ts +++ b/packages/@aws-cdk/aws-sqs/lib/validate-props.ts @@ -1,11 +1,12 @@ +import { toSeconds } from '@aws-cdk/cdk'; import { QueueProps } from './index'; export function validateProps(props: QueueProps) { - validateRange('delivery delay', props.deliveryDelaySec, 0, 900, 'seconds'); + validateRange('delivery delay', toSeconds(props.deliveryDelay), 0, 900, 'seconds'); validateRange('maximum message size', props.maxMessageSizeBytes, 1_024, 262_144, 'bytes'); - validateRange('message retention period', props.retentionPeriodSec, 60, 1_209_600, 'seconds'); - validateRange('receive wait time', props.receiveMessageWaitTimeSec, 0, 20, 'seconds'); - validateRange('visibility timeout', props.visibilityTimeoutSec, 0, 43_200, 'seconds'); + validateRange('message retention period', toSeconds(props.retentionPeriod), 60, 1_209_600, 'seconds'); + validateRange('receive wait time', toSeconds(props.receiveMessageWaitTime), 0, 20, 'seconds'); + validateRange('visibility timeout', toSeconds(props.visibilityTimeout), 0, 43_200, 'seconds'); validateRange('dead letter target maximum receive count', props.deadLetterQueue && props.deadLetterQueue.maxReceiveCount, 1, +Infinity); } diff --git a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index 18c22edfcba05..ee99f1e83b0ba 100644 --- a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts +++ b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts @@ -1,7 +1,7 @@ import { expect, haveResource } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); -import { Stack } from '@aws-cdk/cdk'; +import { Duration, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; import sqs = require('../lib'); import { Queue } from '../lib'; @@ -281,7 +281,7 @@ export = { dimensions: {QueueName: { 'Fn::GetAtt': [ 'Queue4A7E3555', 'QueueName' ] }}, namespace: 'AWS/SQS', metricName: 'NumberOfMessagesSent', - periodSec: 300, + period: Duration.minutes(5), statistic: 'Sum' }); @@ -289,7 +289,7 @@ export = { dimensions: {QueueName: { 'Fn::GetAtt': [ 'Queue4A7E3555', 'QueueName' ] }}, namespace: 'AWS/SQS', metricName: 'SentMessageSize', - periodSec: 300, + period: Duration.minutes(5), statistic: 'Average' }); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts index c9ebca5c49f24..910ca332e9f7e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/invoke-activity.ts @@ -1,4 +1,5 @@ import sfn = require('@aws-cdk/aws-stepfunctions'); +import { Duration } from '@aws-cdk/cdk'; /** * Properties for FunctionTask @@ -11,7 +12,7 @@ export interface InvokeActivityProps { * * @default No heart beat timeout */ - readonly heartbeatSeconds?: number; + readonly heartbeat?: Duration; } /** @@ -28,7 +29,7 @@ export class InvokeActivity implements sfn.IStepFunctionsTask { return { resourceArn: this.activity.activityArn, metricDimensions: { ActivityArn: this.activity.activityArn }, - heartbeatSeconds: this.props.heartbeatSeconds, + heartbeat: this.props.heartbeat, // No IAM permissions necessary, execution role implicitly has Activity permissions. metricPrefixSingular: 'Activity', metricPrefixPlural: 'Activities', diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts index ce10c26947cfc..87c3075371a10 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts @@ -1,6 +1,7 @@ import iam = require('@aws-cdk/aws-iam'); import sqs = require('@aws-cdk/aws-sqs'); import sfn = require('@aws-cdk/aws-stepfunctions'); +import { Duration, toSeconds } from '@aws-cdk/cdk'; /** * Properties for SendMessageTask @@ -18,7 +19,7 @@ export interface SendToQueueProps { * * @default Default value of the queue is used */ - readonly delaySeconds?: number; + readonly delay?: Duration; /** * The token used for deduplication of sent messages. @@ -59,7 +60,7 @@ export class SendToQueue implements sfn.IStepFunctionsTask { QueueUrl: this.queue.queueUrl, ...sfn.FieldUtils.renderObject({ MessageBody: this.props.messageBody.value, - DelaySeconds: this.props.delaySeconds, + DelaySeconds: toSeconds(this.props.delay), MessageDeduplicationId: this.props.messageDeduplicationId, MessageGroupId: this.props.messageGroupId, }) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.ts index 5fae9e8d1819b..5f7f9db9d731f 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.job-poller.ts @@ -13,7 +13,7 @@ class JobPollerStack extends cdk.Stack { task: new tasks.InvokeActivity(submitJobActivity), resultPath: '$.guid', }); - const waitX = new sfn.Wait(this, 'Wait X Seconds', { duration: sfn.WaitDuration.secondsPath('$.wait_time') }); + const waitX = new sfn.Wait(this, 'Wait X Seconds', { time: sfn.WaitTime.secondsPath('$.wait_time') }); const getStatus = new sfn.Task(this, 'Get Job Status', { task: new tasks.InvokeActivity(checkJobActivity), inputPath: '$.guid', @@ -40,11 +40,11 @@ class JobPollerStack extends cdk.Stack { new sfn.StateMachine(this, 'StateMachine', { definition: chain, - timeoutSec: 30 + timeout: cdk.Duration.seconds(30) }); } } const app = new cdk.App(); new JobPollerStack(app, 'aws-stepfunctions-integ'); -app.synth(); \ No newline at end of file +app.synth(); diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md index 0bb4278c64659..be9ccb1a1da14 100644 --- a/packages/@aws-cdk/aws-stepfunctions/README.md +++ b/packages/@aws-cdk/aws-stepfunctions/README.md @@ -70,7 +70,7 @@ const definition = submitJob new sfn.StateMachine(this, 'StateMachine', { definition, - timeoutSec: 300 + timeout: Duration.minutes(5) }); ``` @@ -143,12 +143,12 @@ similar to (for example) `inputPath`. const task = new sfn.Task(this, 'Invoke The Lambda', { task: new tasks.InvokeFunction(myLambda), inputPath: '$.input', - timeoutSeconds: 300, + timeout: Duration.minutes(5), }); // Add a retry policy task.addRetry({ - intervalSeconds: 5, + interval: Duration.seconds(5), maxAttempts: 10 }); diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-graph.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-graph.ts index 50d368493ce63..00f13f69da502 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-graph.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-graph.ts @@ -1,4 +1,5 @@ import iam = require('@aws-cdk/aws-iam'); +import { Duration } from '@aws-cdk/cdk'; import { State } from "./states/state"; /** @@ -32,7 +33,7 @@ export class StateGraph { * * @default No timeout */ - public timeoutSeconds?: number; + public timeout?: Duration; /** * The accumulated policy statements @@ -107,7 +108,7 @@ export class StateGraph { return { StartAt: this.startState.stateId, States: states, - TimeoutSeconds: this.timeoutSeconds + TimeoutSeconds: this.timeout && this.timeout.toSeconds() }; } @@ -154,4 +155,4 @@ export class StateGraph { } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 2341cd8beb1a7..824bc8d8a5333 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -1,6 +1,6 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import iam = require('@aws-cdk/aws-iam'); -import { Construct, IResource, Resource, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, Resource, Stack } from '@aws-cdk/cdk'; import { StateGraph } from './state-graph'; import { CfnStateMachine } from './stepfunctions.generated'; import { IChainable } from './types'; @@ -33,7 +33,7 @@ export interface StateMachineProps { * * @default No timeout */ - readonly timeoutSec?: number; + readonly timeout?: Duration; } /** @@ -94,7 +94,7 @@ export class StateMachine extends StateMachineBase { }); const graph = new StateGraph(props.definition.startState, `State Machine ${id} definition`); - graph.timeoutSeconds = props.timeoutSec; + graph.timeout = props.timeout; const resource = new CfnStateMachine(this, 'Resource', { stateMachineName: props.stateMachineName, diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts index 5dce6925b53d7..2b333b91e3e43 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts @@ -437,7 +437,7 @@ interface CatchTransition { function renderRetry(retry: RetryProps) { return { ErrorEquals: retry.errors, - IntervalSeconds: retry.intervalSeconds, + IntervalSeconds: cdk.toSeconds(retry.interval), MaxAttempts: retry.maxAttempts, BackoffRate: retry.backoffRate }; @@ -507,4 +507,4 @@ export enum StateType { Succeed = 'Succeed', Fail = 'Fail', Parallel = 'Parallel' -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts index e39fd94561b83..10b41d3c7f37b 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts @@ -59,7 +59,7 @@ export interface TaskProps { * * @default 60 */ - readonly timeoutSeconds?: number; + readonly timeout?: cdk.Duration; } /** @@ -74,13 +74,13 @@ export interface TaskProps { */ export class Task extends State implements INextable { public readonly endStates: INextable[]; - private readonly timeoutSeconds?: number; + private readonly timeout?: cdk.Duration; private readonly taskProps: StepFunctionsTaskConfig; constructor(scope: cdk.Construct, id: string, props: TaskProps) { super(scope, id, props); - this.timeoutSeconds = props.timeoutSeconds; + this.timeout = props.timeout; this.taskProps = props.task.bind(this); this.endStates = [this]; } @@ -128,8 +128,8 @@ export class Task extends State implements INextable { Resource: this.taskProps.resourceArn, Parameters: this.taskProps.parameters, ResultPath: renderJsonPath(this.resultPath), - TimeoutSeconds: this.timeoutSeconds, - HeartbeatSeconds: this.taskProps.heartbeatSeconds, + TimeoutSeconds: cdk.toSeconds(this.timeout), + HeartbeatSeconds: cdk.toSeconds(this.taskProps.heartbeat), }; } diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts index 608714ad80c1b..87be700236972 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/wait.ts @@ -3,32 +3,32 @@ import { Chain } from '../chain'; import { IChainable, INextable } from '../types'; import { State, StateType } from './state'; -export class WaitDuration { +export class WaitTime { /** - * Wait a fixed number of seconds + * Wait a fixed amount of time. */ - public static seconds(duration: number) { return new WaitDuration({ Seconds: duration }); } + public static duration(duration: cdk.Duration) { return new WaitTime({ Seconds: duration.toSeconds() }); } /** * Wait until the given ISO8601 timestamp * * @example 2016-03-14T01:59:00Z */ - public static timestamp(timestamp: string) { return new WaitDuration({ Timestamp: timestamp }); } + public static timestamp(timestamp: string) { return new WaitTime({ Timestamp: timestamp }); } /** * Wait for a number of seconds stored in the state object. * * @example $.waitSeconds */ - public static secondsPath(path: string) { return new WaitDuration({ SecondsPath: path }); } + public static secondsPath(path: string) { return new WaitTime({ SecondsPath: path }); } /** * Wait until a timestamp found in the state object. * * @example $.waitTimestamp */ - public static timestampPath(path: string) { return new WaitDuration({ TimestampPath: path }); } + public static timestampPath(path: string) { return new WaitTime({ TimestampPath: path }); } private constructor(private readonly json: any) { } @@ -54,7 +54,7 @@ export interface WaitProps { /** * Wait duration. */ - readonly duration: WaitDuration; + readonly time: WaitTime; } /** @@ -65,12 +65,12 @@ export interface WaitProps { export class Wait extends State implements INextable { public readonly endStates: INextable[]; - private readonly duration: WaitDuration; + private readonly time: WaitTime; constructor(scope: cdk.Construct, id: string, props: WaitProps) { super(scope, id, props); - this.duration = props.duration; + this.time = props.time; this.endStates = [this]; } @@ -89,8 +89,8 @@ export class Wait extends State implements INextable { return { Type: StateType.Wait, Comment: this.comment, - ...this.duration._json, + ...this.time._json, ...this.renderNextEnd(), }; } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/step-functions-task.ts b/packages/@aws-cdk/aws-stepfunctions/lib/step-functions-task.ts index 6a7a756df74b0..a8dabc6658f54 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/step-functions-task.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/step-functions-task.ts @@ -1,5 +1,6 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import iam = require('@aws-cdk/aws-iam'); +import { Duration } from '@aws-cdk/cdk'; import { Task } from './states/task'; /** @@ -47,7 +48,7 @@ export interface StepFunctionsTaskConfig { * * @default No heart beat timeout */ - readonly heartbeatSeconds?: number; + readonly heartbeat?: Duration; /** * Additional policy statements to add to the execution role diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/types.ts b/packages/@aws-cdk/aws-stepfunctions/lib/types.ts index b6e19a127ba98..5116c0382db38 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/types.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/types.ts @@ -1,3 +1,4 @@ +import { Duration } from '@aws-cdk/cdk'; import { Chain } from './chain'; import { State } from './states/state'; @@ -93,9 +94,9 @@ export interface RetryProps { /** * How many seconds to wait initially before retrying * - * @default 1 + * @default Duration.seconds(1) */ - readonly intervalSeconds?: number; + readonly interval?: Duration; /** * How many times to retry this particular error. @@ -143,4 +144,4 @@ export interface CatchProps { /** * Special string value to discard state input, output or result */ -export const DISCARD = 'DISCARD'; \ No newline at end of file +export const DISCARD = 'DISCARD'; diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index cb731048b7467..055110331c074 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -88,8 +88,10 @@ "awslint": { "exclude": [ "import-props-interface:@aws-cdk/aws-stepfunctions.ActivityImportProps", - "export:@aws-cdk/aws-stepfunctions.IActivity" + "export:@aws-cdk/aws-stepfunctions.IActivity", + "duration-prop-type:@aws-cdk/aws-stepfunctions.WaitProps.duration", + "duration-prop-type:@aws-cdk/aws-stepfunctions.Errors.Timeout" ] }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions/test/test.activity.ts b/packages/@aws-cdk/aws-stepfunctions/test/test.activity.ts index 5b45bdee74467..f0775cc2e4168 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/test.activity.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/test.activity.ts @@ -28,7 +28,7 @@ export = { // THEN const sharedMetric = { - periodSec: 300, + period: cdk.Duration.minutes(5), namespace: 'AWS/States', dimensions: { ActivityArn: { Ref: 'Activity04690B0A' }}, }; @@ -46,4 +46,4 @@ export = { test.done(); } -}; \ No newline at end of file +}; diff --git a/packages/@aws-cdk/aws-stepfunctions/test/test.states-language.ts b/packages/@aws-cdk/aws-stepfunctions/test/test.states-language.ts index edc794c6f126b..a4c2d50e5c45b 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/test.states-language.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/test.states-language.ts @@ -102,7 +102,9 @@ export = { const task1 = new stepfunctions.Pass(stack, 'State One'); const task2 = new stepfunctions.Pass(stack, 'State Two'); - const task3 = new stepfunctions.Wait(stack, 'State Three', { duration: stepfunctions.WaitDuration.seconds(10) }); + const task3 = new stepfunctions.Wait(stack, 'State Three', { + time: stepfunctions.WaitTime.duration(cdk.Duration.seconds(10)) + }); // WHEN const chain = stepfunctions.Chain diff --git a/packages/@aws-cdk/cdk/lib/cfn-resource.ts b/packages/@aws-cdk/cdk/lib/cfn-resource.ts index 38317256cf8f3..253dd2a8153ce 100644 --- a/packages/@aws-cdk/cdk/lib/cfn-resource.ts +++ b/packages/@aws-cdk/cdk/lib/cfn-resource.ts @@ -222,7 +222,7 @@ export class CfnResource extends CfnRefElement { Type: this.resourceType, Properties: ignoreEmpty(properties), DependsOn: ignoreEmpty(renderDependsOn(this.dependsOn)), - CreationPolicy: capitalizePropertyNames(this, this.options.creationPolicy), + CreationPolicy: capitalizePropertyNames(this, renderCreationPolicy(this.options.creationPolicy)), UpdatePolicy: capitalizePropertyNames(this, this.options.updatePolicy), UpdateReplacePolicy: capitalizePropertyNames(this, this.options.updateReplacePolicy), DeletionPolicy: capitalizePropertyNames(this, this.options.deletionPolicy), @@ -261,6 +261,18 @@ export class CfnResource extends CfnRefElement { .sort((x, y) => x.node.path.localeCompare(y.node.path)) .map(r => r.logicalId); } + + function renderCreationPolicy(policy: CreationPolicy | undefined): any { + if (!policy) { return undefined; } + const result: any = { ...policy }; + if (policy.resourceSignal && policy.resourceSignal.timeout) { + result.resourceSignal = { + ...policy.resourceSignal, + timeout: policy.resourceSignal.timeout.toISOString(), + }; + } + return result; + } } protected renderProperties(properties: any): { [key: string]: any } { diff --git a/packages/@aws-cdk/cdk/lib/duration.ts b/packages/@aws-cdk/cdk/lib/duration.ts new file mode 100644 index 0000000000000..d8d8ca7f23a3e --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/duration.ts @@ -0,0 +1,208 @@ +/** + * Represents a length of time. + */ +export class Duration { + /** + * @param amount the amount of Seconds the `Duration` will represent. + * @returns a new `Duration` representing `amount` Seconds. + */ + public static seconds(amount: number): Duration { + return new Duration(amount, TimeUnit.Seconds); + } + + /** + * @param amount the amount of Minutes the `Duration` will represent. + * @returns a new `Duration` representing `amount` Minutes. + */ + public static minutes(amount: number): Duration { + return new Duration(amount, TimeUnit.Minutes); + } + + /** + * @param amount the amount of Hours the `Duration` will represent. + * @returns a new `Duration` representing `amount` Hours. + */ + public static hours(amount: number): Duration { + return new Duration(amount, TimeUnit.Hours); + } + + /** + * @param amount the amount of Days the `Duration` will represent. + * @returns a new `Duration` representing `amount` Days. + */ + public static days(amount: number): Duration { + return new Duration(amount, TimeUnit.Days); + } + + /** + * @param duration an ISO-formtted duration to be parsed. + * @returns the parsed `Duration`. + */ + public static parse(duration: string): Duration { + const matches = duration.match(/^PT(?:(\d+)D)?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/); + if (!matches) { + throw new Error(`Not a valid ISO duration: ${duration}`); + } + const [, days, hours, minutes, seconds] = matches; + if (!days && !hours && !minutes && !seconds) { + throw new Error(`Not a valid ISO duration: ${duration}`); + } + return Duration.seconds( + _toInt(seconds) + + (_toInt(minutes) * 60) + + (_toInt(hours) * 3_600) + + (_toInt(days) * 86_400) + ); + + function _toInt(str: string): number { + if (!str) { return 0; } + return Number(str); + } + } + + private readonly amount: number; + private readonly unit: TimeUnit; + + private constructor(amount: number, unit: TimeUnit) { + if (amount < 0) { + throw new Error(`Duration amounts cannot be negative. Received: ${amount}`); + } + + this.amount = amount; + this.unit = unit; + } + + /** + * @returns the value of this `Duration` expressed in Seconds. + */ + public toSeconds(_opts: TimeConversionOptions = {}): number { + switch (this.unit) { + case TimeUnit.Seconds: + return this.amount; + case TimeUnit.Minutes: + return this.amount * 60; + case TimeUnit.Hours: + return this.amount * 3600; + case TimeUnit.Days: + return this.amount * 86_400; + default: + throw new Error(`Unexpected time unit: ${this.unit}`); + } + } + + public toMinutes(opts: TimeConversionOptions = {}): number { + switch (this.unit) { + case TimeUnit.Seconds: + return this.safeConversion(60, opts, TimeUnit.Seconds); + case TimeUnit.Minutes: + return this.amount; + case TimeUnit.Hours: + return this.amount * 60; + case TimeUnit.Days: + return this.amount * 1_440; + default: + throw new Error(`Unexpected time unit: ${this.unit}`); + } + } + + /** + * @returns the value of this `Duration` expressed in Days. + */ + public toDays(opts: TimeConversionOptions = {}): number { + switch (this.unit) { + case TimeUnit.Seconds: + return this.safeConversion(86_400, opts, TimeUnit.Days); + case TimeUnit.Minutes: + return this.safeConversion(1_440, opts, TimeUnit.Days); + case TimeUnit.Hours: + return this.safeConversion(24, opts, TimeUnit.Days); + case TimeUnit.Days: + return this.amount; + default: + throw new Error(`Unexpected time unit: ${this.unit}`); + } + } + + /** + * @returns ISO representation of this period. + */ + public toISOString(): string { + if (this.amount === 0) { return 'PT0S'; } + switch (this.unit) { + case TimeUnit.Seconds: + return `PT${this.fractionDuration('S', 60, Duration.minutes)}`; + case TimeUnit.Minutes: + return `PT${this.fractionDuration('M', 60, Duration.hours)}`; + case TimeUnit.Hours: + return `PT${this.fractionDuration('H', 24, Duration.days)}`; + case TimeUnit.Days: + return `PT${this.amount}D`; + default: + throw new Error(`Unexpected time unit: ${this.unit}`); + } + } + + public toString(): string { + return `${this.amount} ${this.unit}`; + } + + /** + * Divides the current duration by a certain amount and throws an exception if `opts` requires `integral` conversion + * and the requirement is not satisfied. + * + * @param divisor the value to divide `this.amount` by. + * @param opts conversion options instructing whether integral conversion is required or not. + * @param targetUnit the unit of time we are converting to, so it can be used in the error message when needed + * + * @returns the result of `this.amount / divisor`. + */ + private safeConversion(divisor: number, { integral = true }: TimeConversionOptions, targetUnit: TimeUnit): number { + const remainder = this.amount % divisor; + if (integral && remainder !== 0) { + throw new Error(`Impossible intergal conversion of ${this} to ${targetUnit} was requested`); + } + return this.amount / divisor; + } + + private fractionDuration(symbol: string, modulus: number, next: (amount: number) => Duration): string { + if (this.amount < modulus) { + return `${this.amount}${symbol}`; + } + const remainder = this.amount % modulus; + const quotient = next((this.amount - remainder) / modulus).toISOString().slice(2); + return remainder > 0 + ? `${quotient}${remainder}${symbol}` + : quotient; + } +} + +/** + * Options for how to convert time to a different unit. + */ +export interface TimeConversionOptions { + /** + * If `true`, conversions into a larger time unit (e.g. `Seconds` to `Mintues`) will fail if the result is not an + * integer. + * + * @default true + */ + readonly integral?: boolean; +} + +const enum TimeUnit { + Seconds = 's', + Minutes = 'minutes', + Hours = 'hours', + Days = 'days' +} + +/** + * Helpful syntax sugar for turning an optional `Duration` into a count of seconds. + * + * @param duration an optional duration to convert into an amount of sunctions. + * + * @return `duration && duration.toSeconds()`. + */ +export function toSeconds(duration: Duration | undefined): number | undefined { + return duration && duration.toSeconds(); +} diff --git a/packages/@aws-cdk/cdk/lib/index.ts b/packages/@aws-cdk/cdk/lib/index.ts index bc99eb685e1cf..657afc156b982 100644 --- a/packages/@aws-cdk/cdk/lib/index.ts +++ b/packages/@aws-cdk/cdk/lib/index.ts @@ -27,6 +27,7 @@ export * from './cfn-dynamic-reference'; export * from './tag'; export * from './removal-policy'; export * from './arn'; +export * from './duration'; export * from './app'; export * from './context'; diff --git a/packages/@aws-cdk/cdk/lib/resource-policy.ts b/packages/@aws-cdk/cdk/lib/resource-policy.ts index a3ff6b9d84691..20bc69c8f8d42 100644 --- a/packages/@aws-cdk/cdk/lib/resource-policy.ts +++ b/packages/@aws-cdk/cdk/lib/resource-policy.ts @@ -1,3 +1,5 @@ +import { Duration } from "./duration"; + /** * Associate the CreationPolicy attribute with a resource to prevent its status from reaching create complete until * AWS CloudFormation receives a specified number of success signals or the timeout period is exceeded. To signal a @@ -61,7 +63,7 @@ export interface ResourceSignal { * The timeout period starts after AWS CloudFormation starts creating the resource, and the timeout expires no sooner * than the time you specify but can occur shortly thereafter. The maximum time that you can specify is 12 hours. */ - readonly timeout?: string; + readonly timeout?: Duration; } /** diff --git a/packages/@aws-cdk/cdk/test/test.duration.ts b/packages/@aws-cdk/cdk/test/test.duration.ts new file mode 100644 index 0000000000000..7996d7fc86e73 --- /dev/null +++ b/packages/@aws-cdk/cdk/test/test.duration.ts @@ -0,0 +1,91 @@ +import nodeunit = require('nodeunit'); +import { Duration } from '../lib'; + +export = nodeunit.testCase({ + 'negative amount'(test: nodeunit.Test) { + test.throws(() => Duration.seconds(-1), /negative/); + + test.done(); + }, + + 'Duration in seconds'(test: nodeunit.Test) { + const duration = Duration.seconds(300); + + test.equal(duration.toSeconds(), 300); + test.equal(duration.toMinutes(), 5); + test.throws(() => duration.toDays(), /Impossible intergal conversion of/); + test.equal(duration.toDays({ integral: false }), 300 / 86_400); + + test.equal(Duration.seconds(60 * 60 * 24).toDays(), 1); + + test.done(); + }, + + 'Duration in minutes'(test: nodeunit.Test) { + const duration = Duration.minutes(5); + + test.equal(duration.toSeconds(), 300); + test.equal(duration.toMinutes(), 5); + test.throws(() => duration.toDays(), /Impossible intergal conversion of/); + test.equal(duration.toDays({ integral: false }), 300 / 86_400); + + test.equal(Duration.minutes(60 * 24).toDays(), 1); + + test.done(); + }, + + 'Duration in hours'(test: nodeunit.Test) { + const duration = Duration.hours(5); + + test.equal(duration.toSeconds(), 18_000); + test.equal(duration.toMinutes(), 300); + test.throws(() => duration.toDays(), /Impossible intergal conversion of/); + test.equals(duration.toDays({ integral: false }), 5 / 24); + + test.equal(Duration.hours(24).toDays(), 1); + + test.done(); + }, + + 'Duration in days'(test: nodeunit.Test) { + const duration = Duration.days(1); + + test.equal(duration.toSeconds(), 86_400); + test.equal(duration.toMinutes(), 1_440); + test.equal(duration.toDays(), 1); + + test.done(); + }, + + 'toISOString'(test: nodeunit.Test) { + test.equal(Duration.seconds(0).toISOString(), 'PT0S'); + test.equal(Duration.minutes(0).toISOString(), 'PT0S'); + test.equal(Duration.hours(0).toISOString(), 'PT0S'); + test.equal(Duration.days(0).toISOString(), 'PT0S'); + + test.equal(Duration.seconds(5).toISOString(), 'PT5S'); + test.equal(Duration.minutes(5).toISOString(), 'PT5M'); + test.equal(Duration.hours(5).toISOString(), 'PT5H'); + test.equal(Duration.days(5).toISOString(), 'PT5D'); + + test.equal(Duration.seconds(1 + 60 * (1 + 60 * (1 + 24))).toISOString(), 'PT1D1H1M1S'); + + test.done(); + }, + + 'parse'(test: nodeunit.Test) { + test.equal(Duration.parse('PT0S').toSeconds(), 0); + test.equal(Duration.parse('PT0M').toSeconds(), 0); + test.equal(Duration.parse('PT0H').toSeconds(), 0); + test.equal(Duration.parse('PT0D').toSeconds(), 0); + + test.equal(Duration.parse('PT5S').toSeconds(), 5); + test.equal(Duration.parse('PT5M').toSeconds(), 300); + test.equal(Duration.parse('PT5H').toSeconds(), 18_000); + test.equal(Duration.parse('PT5D').toSeconds(), 432_000); + + test.equal(Duration.parse('PT1D1H1M1S').toSeconds(), 1 + 60 * (1 + 60 * (1 + 24))); + + test.done(); + } +}); diff --git a/tools/awslint/bin/awslint.ts b/tools/awslint/bin/awslint.ts index 83d2c5c3e09ac..d34e2d8f3c767 100644 --- a/tools/awslint/bin/awslint.ts +++ b/tools/awslint/bin/awslint.ts @@ -5,8 +5,8 @@ import fs = require('fs-extra'); import reflect = require('jsii-reflect'); import path = require('path'); import yargs = require('yargs'); -import { AggregateLinter, apiLinter, attributesLinter, cfnResourceLinter, constructLinter, DiagnosticLevel, eventsLinter, exportsLinter, importsLinter, - integrationLinter, moduleLinter, resourceLinter } from '../lib'; +import { AggregateLinter, apiLinter, attributesLinter, cfnResourceLinter, constructLinter, DiagnosticLevel, durationsLinter, eventsLinter, exportsLinter, + importsLinter, integrationLinter, moduleLinter, resourceLinter } from '../lib'; const linter = new AggregateLinter( moduleLinter, @@ -18,7 +18,8 @@ const linter = new AggregateLinter( attributesLinter, exportsLinter, eventsLinter, - integrationLinter + integrationLinter, + durationsLinter ); let stackTrace = false; @@ -273,4 +274,4 @@ async function shell(command: string) { }); child.once('error', ko); }); -} \ No newline at end of file +} diff --git a/tools/awslint/lib/rules/durations.ts b/tools/awslint/lib/rules/durations.ts new file mode 100644 index 0000000000000..8a58962ab10e5 --- /dev/null +++ b/tools/awslint/lib/rules/durations.ts @@ -0,0 +1,51 @@ +import { Property } from 'jsii-reflect'; +import { Linter } from '../linter'; + +const DURATION_FQN = '@aws-cdk/cdk.Duration'; +const DURATION_SUFFIX = /(Days|Milli(?:(?:S|s)econd)?s?|Sec(?:ond)?s?)$/; + +export const durationsLinter = new Linter(assm => { + const result = new Array(); + const generatedClassesPrefix = `${assm.name}.Cfn`; + for (const type of assm.types) { + // L1 classes are exempted from this rule, doing basic name matching here... + if (type.fqn.startsWith(generatedClassesPrefix)) { continue; } + if (!type.isClassType() && !type.isDataType() && !type.isInterfaceType()) { continue; } + for (const property of type.allProperties) { + if (isDurationProperty(property)) { + result.push(property); + } + } + } + return result; + + function isDurationProperty(prop: Property): boolean { + const lowerCaseName = prop.name.toLowerCase(); + // No lookbehind in JS regexes, so excluding "*PerSecond" by hand here... + return (DURATION_SUFFIX.test(prop.name) && !/PerSecond$/.test(prop.name)) + || lowerCaseName.endsWith('duration') + || lowerCaseName.endsWith('period') + || lowerCaseName.endsWith('timeout') + || lowerCaseName.endsWith('ttl') + || prop.type.fqn === DURATION_FQN; + } +}); + +durationsLinter.add({ + code: 'duration-prop-type', + message: `property must be typed ${DURATION_FQN}`, + eval: evaluation => { + evaluation.assert(evaluation.ctx.type.fqn === DURATION_FQN, + `${evaluation.ctx.parentType.fqn}.${evaluation.ctx.name}`); + }, +}); + +durationsLinter.add({ + code: 'duration-prop-name', + message: 'property must not use time-unit suffix', + eval: evaluation => { + evaluation.assert(!DURATION_SUFFIX.test(evaluation.ctx.name), + `${evaluation.ctx.parentType.fqn}.${evaluation.ctx.name}`, + `(suggested name: "${evaluation.ctx.name.replace(DURATION_SUFFIX, '')}")`); + }, +}); diff --git a/tools/awslint/lib/rules/index.ts b/tools/awslint/lib/rules/index.ts index 04dffc0af190b..b5fd25c16295a 100644 --- a/tools/awslint/lib/rules/index.ts +++ b/tools/awslint/lib/rules/index.ts @@ -1,10 +1,11 @@ -export * from './construct'; -export * from './module'; -export * from './resource'; -export * from './imports'; -export * from './cfn-resource'; -export * from './attributes'; export * from './api'; -export * from './exports'; +export * from './attributes'; +export * from './cfn-resource'; export * from './cloudwatch-events'; +export * from './construct'; +export * from './durations'; +export * from './exports'; +export * from './imports'; export * from './integrations'; +export * from './module'; +export * from './resource'; From 5f9ca7a9783f8944596e0bbcba6d617ec626ed7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Tue, 18 Jun 2019 10:11:38 +0200 Subject: [PATCH 2/5] Stop the switch madness! --- packages/@aws-cdk/cdk/lib/duration.ts | 95 +++++++-------------- packages/@aws-cdk/cdk/test/test.duration.ts | 20 +++-- 2 files changed, 47 insertions(+), 68 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/duration.ts b/packages/@aws-cdk/cdk/lib/duration.ts index d8d8ca7f23a3e..083d7a7627817 100644 --- a/packages/@aws-cdk/cdk/lib/duration.ts +++ b/packages/@aws-cdk/cdk/lib/duration.ts @@ -75,52 +75,22 @@ export class Duration { /** * @returns the value of this `Duration` expressed in Seconds. */ - public toSeconds(_opts: TimeConversionOptions = {}): number { - switch (this.unit) { - case TimeUnit.Seconds: - return this.amount; - case TimeUnit.Minutes: - return this.amount * 60; - case TimeUnit.Hours: - return this.amount * 3600; - case TimeUnit.Days: - return this.amount * 86_400; - default: - throw new Error(`Unexpected time unit: ${this.unit}`); - } + public toSeconds(opts: TimeConversionOptions = {}): number { + return _ensureIntegral(this.amount * this.unit.inSeconds, opts); } + /** + * @returns the value of this `Duration` expressed in Minutes. + */ public toMinutes(opts: TimeConversionOptions = {}): number { - switch (this.unit) { - case TimeUnit.Seconds: - return this.safeConversion(60, opts, TimeUnit.Seconds); - case TimeUnit.Minutes: - return this.amount; - case TimeUnit.Hours: - return this.amount * 60; - case TimeUnit.Days: - return this.amount * 1_440; - default: - throw new Error(`Unexpected time unit: ${this.unit}`); - } + return _ensureIntegral(this.amount * this.unit.inMinutes, opts); } /** * @returns the value of this `Duration` expressed in Days. */ public toDays(opts: TimeConversionOptions = {}): number { - switch (this.unit) { - case TimeUnit.Seconds: - return this.safeConversion(86_400, opts, TimeUnit.Days); - case TimeUnit.Minutes: - return this.safeConversion(1_440, opts, TimeUnit.Days); - case TimeUnit.Hours: - return this.safeConversion(24, opts, TimeUnit.Days); - case TimeUnit.Days: - return this.amount; - default: - throw new Error(`Unexpected time unit: ${this.unit}`); - } + return _ensureIntegral(this.amount * this.unit.inDays, opts); } /** @@ -146,24 +116,6 @@ export class Duration { return `${this.amount} ${this.unit}`; } - /** - * Divides the current duration by a certain amount and throws an exception if `opts` requires `integral` conversion - * and the requirement is not satisfied. - * - * @param divisor the value to divide `this.amount` by. - * @param opts conversion options instructing whether integral conversion is required or not. - * @param targetUnit the unit of time we are converting to, so it can be used in the error message when needed - * - * @returns the result of `this.amount / divisor`. - */ - private safeConversion(divisor: number, { integral = true }: TimeConversionOptions, targetUnit: TimeUnit): number { - const remainder = this.amount % divisor; - if (integral && remainder !== 0) { - throw new Error(`Impossible intergal conversion of ${this} to ${targetUnit} was requested`); - } - return this.amount / divisor; - } - private fractionDuration(symbol: string, modulus: number, next: (amount: number) => Duration): string { if (this.amount < modulus) { return `${this.amount}${symbol}`; @@ -189,13 +141,6 @@ export interface TimeConversionOptions { readonly integral?: boolean; } -const enum TimeUnit { - Seconds = 's', - Minutes = 'minutes', - Hours = 'hours', - Days = 'days' -} - /** * Helpful syntax sugar for turning an optional `Duration` into a count of seconds. * @@ -206,3 +151,29 @@ const enum TimeUnit { export function toSeconds(duration: Duration | undefined): number | undefined { return duration && duration.toSeconds(); } + +class TimeUnit { + public static readonly Seconds = new TimeUnit('seconds', 1); + public static readonly Minutes = new TimeUnit('minutes', 60); + public static readonly Hours = new TimeUnit('hours', 3_600); + public static readonly Days = new TimeUnit('days', 86_400); + + public readonly inMinutes: number; + public readonly inDays: number; + + private constructor(public readonly label: string, public readonly inSeconds: number) { + this.inMinutes = inSeconds / 60; + this.inDays = inSeconds / 86_400; + } + + public toString() { + return this.label; + } +} + +function _ensureIntegral(value: number, { integral = true }: TimeConversionOptions) { + if (!Number.isInteger(value) && integral) { + throw new Error(`Required integral time unit conversion, but value ${value} is not integral.`); + } + return value; +} diff --git a/packages/@aws-cdk/cdk/test/test.duration.ts b/packages/@aws-cdk/cdk/test/test.duration.ts index 7996d7fc86e73..8aeed3b83d1e0 100644 --- a/packages/@aws-cdk/cdk/test/test.duration.ts +++ b/packages/@aws-cdk/cdk/test/test.duration.ts @@ -13,8 +13,8 @@ export = nodeunit.testCase({ test.equal(duration.toSeconds(), 300); test.equal(duration.toMinutes(), 5); - test.throws(() => duration.toDays(), /Impossible intergal conversion of/); - test.equal(duration.toDays({ integral: false }), 300 / 86_400); + test.throws(() => duration.toDays(), /Required integral time unit conversion, but value/); + floatEqual(test, duration.toDays({ integral: false }), 300 / 86_400); test.equal(Duration.seconds(60 * 60 * 24).toDays(), 1); @@ -26,8 +26,8 @@ export = nodeunit.testCase({ test.equal(duration.toSeconds(), 300); test.equal(duration.toMinutes(), 5); - test.throws(() => duration.toDays(), /Impossible intergal conversion of/); - test.equal(duration.toDays({ integral: false }), 300 / 86_400); + test.throws(() => duration.toDays(), /Required integral time unit conversion, but value/); + floatEqual(test, duration.toDays({ integral: false }), 300 / 86_400); test.equal(Duration.minutes(60 * 24).toDays(), 1); @@ -39,8 +39,8 @@ export = nodeunit.testCase({ test.equal(duration.toSeconds(), 18_000); test.equal(duration.toMinutes(), 300); - test.throws(() => duration.toDays(), /Impossible intergal conversion of/); - test.equals(duration.toDays({ integral: false }), 5 / 24); + test.throws(() => duration.toDays(), /Required integral time unit conversion, but value/); + floatEqual(test, duration.toDays({ integral: false }), 5 / 24); test.equal(Duration.hours(24).toDays(), 1); @@ -89,3 +89,11 @@ export = nodeunit.testCase({ test.done(); } }); + +function floatEqual(test: nodeunit.Test, actual: number, expected: number) { + test.ok( + // Floats are subject to rounding errors up to Number.ESPILON + actual >= expected - Number.EPSILON && actual <= expected + Number.EPSILON, + `${actual} == ${expected}`, + ); +} From d1ef198769875e00271c42752a2502b0cb9528e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Jun 2019 12:19:57 +0200 Subject: [PATCH 3/5] PR Feedback 1. Dropped the free-floating toSeconds function 2. Added test for unresolved tokens in constructor 3. Made .toString() return a breaking token. --- packages/@aws-cdk/aws-apigateway/lib/stage.ts | 2 +- .../lib/step-scaling-action.ts | 2 +- .../lib/target-tracking-scaling-policy.ts | 4 ++-- .../aws-autoscaling/lib/lifecycle-hook.ts | 2 +- .../lib/step-scaling-action.ts | 2 +- .../lib/target-tracking-scaling-policy.ts | 2 +- .../aws-cloudfront/lib/web_distribution.ts | 10 ++++---- .../@aws-cdk/aws-ecs/lib/base/base-service.ts | 2 +- .../aws-ecs/lib/container-definition.ts | 2 +- packages/@aws-cdk/aws-iam/lib/role.ts | 2 +- packages/@aws-cdk/aws-lambda/lib/function.ts | 2 +- packages/@aws-cdk/aws-rds/lib/instance.ts | 2 +- packages/@aws-cdk/aws-sqs/lib/queue.ts | 8 +++---- .../@aws-cdk/aws-sqs/lib/validate-props.ts | 8 +++---- .../lib/send-to-queue.ts | 2 +- .../aws-stepfunctions/lib/states/state.ts | 2 +- .../aws-stepfunctions/lib/states/task.ts | 4 ++-- packages/@aws-cdk/cdk/lib/duration.ts | 23 +++++++++---------- packages/@aws-cdk/cdk/test/test.duration.ts | 8 ++++++- 19 files changed, 47 insertions(+), 42 deletions(-) diff --git a/packages/@aws-cdk/aws-apigateway/lib/stage.ts b/packages/@aws-cdk/aws-apigateway/lib/stage.ts index 6e4000ca756ba..a633c6f5af50a 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/stage.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/stage.ts @@ -256,7 +256,7 @@ export class Stage extends Resource { return { httpMethod, resourcePath, cacheDataEncrypted: options.cacheDataEncrypted, - cacheTtlInSeconds: toSeconds(options.cacheTtl), + cacheTtlInSeconds: options.cacheTtl && options.cacheTtl.toSeconds(), cachingEnabled: options.cachingEnabled, dataTraceEnabled: options.dataTraceEnabled, loggingLevel: options.loggingLevel, diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts index bb9fe106377c8..9018bff1f665f 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-action.ts @@ -86,7 +86,7 @@ export class StepScalingAction extends cdk.Construct { scalingTargetId: props.scalingTarget.scalableTargetId, stepScalingPolicyConfiguration: { adjustmentType: props.adjustmentType, - cooldown: cdk.toSeconds(props.cooldown), + cooldown: props.cooldown && props.cooldown.toSeconds(), minAdjustmentMagnitude: props.minAdjustmentMagnitude, metricAggregationType: props.metricAggregationType, stepAdjustments: cdk.Lazy.anyValue({ produce: () => this.adjustments }), diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts index 4c653c5a721e8..316ac0479c0b2 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts @@ -128,8 +128,8 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { predefinedMetricType: props.predefinedMetric, resourceLabel: props.resourceLabel, } : undefined, - scaleInCooldown: cdk.toSeconds(props.scaleInCooldown), - scaleOutCooldown: cdk.toSeconds(props.scaleOutCooldown), + scaleInCooldown: props.scaleInCooldown && props.scaleInCooldown.toSeconds(), + scaleOutCooldown: props.scaleOutCooldown && props.scaleOutCooldown.toSeconds(), targetValue: props.targetValue } }); diff --git a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts index 4292008bc49c0..daea56c782044 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts @@ -103,7 +103,7 @@ export class LifecycleHook extends Resource implements ILifecycleHook { const resource = new CfnLifecycleHook(this, 'Resource', { autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, defaultResult: props.defaultResult, - heartbeatTimeout: toSeconds(props.heartbeatTimeout), + heartbeatTimeout: props.heartbeatTimeout && props.heartbeatTimeout.toSeconds(), lifecycleHookName: props.lifecycleHookName, lifecycleTransition: props.lifecycleTransition, notificationMetadata: props.notificationMetadata, diff --git a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts index 892010ec9d247..f55430c5e2668 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts @@ -74,7 +74,7 @@ export class StepScalingAction extends cdk.Construct { policyType: 'StepScaling', autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, cooldown: props.cooldown && props.cooldown.toSeconds().toString(), - estimatedInstanceWarmup: cdk.toSeconds(props.estimatedInstanceWarmup), + estimatedInstanceWarmup: props.estimatedInstanceWarmup && props.estimatedInstanceWarmup.toSeconds(), adjustmentType: props.adjustmentType, minAdjustmentMagnitude: props.minAdjustmentMagnitude, metricAggregationType: props.metricAggregationType, diff --git a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts index 29c65923dace5..d3fdfb5712abb 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts @@ -123,7 +123,7 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { policyType: 'TargetTrackingScaling', autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, cooldown: props.cooldown && props.cooldown.toSeconds().toString(), - estimatedInstanceWarmup: cdk.toSeconds(props.estimatedInstanceWarmup), + estimatedInstanceWarmup: props.estimatedInstanceWarmup && props.estimatedInstanceWarmup.toSeconds(), targetTrackingConfiguration: { customizedMetricSpecification: renderCustomMetric(props.customMetric), disableScaleIn: props.disableScaleIn, diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts index f60ed7a968ff6..fa05d34e803ec 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts @@ -606,8 +606,8 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib ? { httpPort: originConfig.customOriginSource.httpPort || 80, httpsPort: originConfig.customOriginSource.httpsPort || 443, - originKeepaliveTimeout: cdk.toSeconds(originConfig.customOriginSource.originKeepaliveTimeout) || 5, - originReadTimeout: cdk.toSeconds(originConfig.customOriginSource.originReadTimeout) || 30, + originKeepaliveTimeout: originConfig.customOriginSource.originKeepaliveTimeout && originConfig.customOriginSource.originKeepaliveTimeout.toSeconds() || 5, + originReadTimeout: originConfig.customOriginSource.originReadTimeout && originConfig.customOriginSource.originReadTimeout.toSeconds() || 30, originProtocolPolicy: originConfig.customOriginSource.originProtocolPolicy || OriginProtocolPolicy.HttpsOnly, originSslProtocols: originConfig.customOriginSource.allowedOriginSSLVersions || [OriginSslPolicy.TLSv1_2] } @@ -704,10 +704,10 @@ export class CloudFrontWebDistribution extends cdk.Construct implements IDistrib allowedMethods: this.METHOD_LOOKUP_MAP[input.allowedMethods || CloudFrontAllowedMethods.GET_HEAD], cachedMethods: this.METHOD_LOOKUP_MAP[input.cachedMethods || CloudFrontAllowedCachedMethods.GET_HEAD], compress: input.compress, - defaultTtl: cdk.toSeconds(input.defaultTtl), + defaultTtl: input.defaultTtl && input.defaultTtl.toSeconds(), forwardedValues: input.forwardedValues || { queryString: false, cookies: { forward: "none" } }, - maxTtl: cdk.toSeconds(input.maxTtl), - minTtl: cdk.toSeconds(input.minTtl), + maxTtl: input.maxTtl && input.maxTtl.toSeconds(), + minTtl: input.minTtl && input.minTtl.toSeconds(), trustedSigners: input.trustedSigners, targetOriginId: input.targetOriginId, viewerProtocolPolicy: protoPolicy || ViewerProtocolPolicy.RedirectToHTTPS, diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index 423f048993954..ca82535c66ccd 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -150,7 +150,7 @@ export abstract class BaseService extends Resource maximumPercent: props.maximumPercent || 200, minimumHealthyPercent: props.minimumHealthyPercent === undefined ? 50 : props.minimumHealthyPercent }, - healthCheckGracePeriodSeconds: toSeconds(props.healthCheckGracePeriod), + healthCheckGracePeriodSeconds: props.healthCheckGracePeriod && props.healthCheckGracePeriod.toSeconds(), /* role: never specified, supplanted by Service Linked Role */ networkConfiguration: Lazy.anyValue({ produce: () => this.networkConfiguration }), serviceRegistries: Lazy.anyValue({ produce: () => this.serviceRegistries }), diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index daca16ee46e25..61d284f32d558 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -479,7 +479,7 @@ function renderHealthCheck(hc: HealthCheck): CfnTaskDefinition.HealthCheckProper command: getHealthCheckCommand(hc), interval: hc.interval != null ? hc.interval.toSeconds() : 30, retries: hc.retries !== undefined ? hc.retries : 3, - startPeriod: cdk.toSeconds(hc.startPeriod), + startPeriod: hc.startPeriod && hc.startPeriod.toSeconds(), timeout: hc.timeout !== undefined ? hc.timeout.toSeconds() : 5, }; } diff --git a/packages/@aws-cdk/aws-iam/lib/role.ts b/packages/@aws-cdk/aws-iam/lib/role.ts index 53b19ac53fd8a..04d5403fcfe0e 100644 --- a/packages/@aws-cdk/aws-iam/lib/role.ts +++ b/packages/@aws-cdk/aws-iam/lib/role.ts @@ -196,7 +196,7 @@ export class Role extends Resource implements IRole { this.assumeRolePolicy = createAssumeRolePolicy(props.assumedBy, props.externalId); this.managedPolicyArns = props.managedPolicyArns || [ ]; - const maxSessionDuration = toSeconds(props.maxSessionDuration); + const maxSessionDuration = props.maxSessionDuration && props.maxSessionDuration.toSeconds(); validateMaxSessionDuration(maxSessionDuration); const role = new CfnRole(this, 'Resource', { diff --git a/packages/@aws-cdk/aws-lambda/lib/function.ts b/packages/@aws-cdk/aws-lambda/lib/function.ts index 806445b27dc47..22db76797d5f2 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function.ts @@ -427,7 +427,7 @@ export class Function extends FunctionBase { code: Lazy.anyValue({ produce: () => props.code._toJSON(resource) }), layers: Lazy.listValue({ produce: () => this.layers.map(layer => layer.layerVersionArn) }, { omitEmpty: true }), handler: props.handler, - timeout: toSeconds(props.timeout), + timeout: props.timeout && props.timeout.toSeconds(), runtime: props.runtime.name, role: this.role.roleArn, environment: Lazy.anyValue({ produce: () => this.renderEnvironment() }), diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index 4a3c063ee0b45..0e832f40fee56 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -518,7 +518,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData enableIamDatabaseAuthentication: props.iamAuthentication, enablePerformanceInsights: props.enablePerformanceInsights, iops, - monitoringInterval: toSeconds(props.monitoringInterval), + monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(), monitoringRoleArn: monitoringRole && monitoringRole.roleArn, multiAz: props.multiAz, optionGroupName: props.optionGroup && props.optionGroup.optionGroupName, diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 485eb1d5b15c5..ff29b57729fc0 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -248,11 +248,11 @@ export class Queue extends QueueBase { ...this.determineFifoProps(props), ...encryptionProps, redrivePolicy, - delaySeconds: toSeconds(props.deliveryDelay), + delaySeconds: props.deliveryDelay && props.deliveryDelay.toSeconds(), maximumMessageSize: props.maxMessageSizeBytes, - messageRetentionPeriod: toSeconds(props.retentionPeriod), - receiveMessageWaitTimeSeconds: toSeconds(props.receiveMessageWaitTime), - visibilityTimeout: toSeconds(props.visibilityTimeout), + messageRetentionPeriod: props.retentionPeriod && props.retentionPeriod.toSeconds(), + receiveMessageWaitTimeSeconds: props.receiveMessageWaitTime && props.receiveMessageWaitTime.toSeconds(), + visibilityTimeout: props.visibilityTimeout && props.visibilityTimeout.toSeconds(), }); this.encryptionMasterKey = encryptionMasterKey; this.queueArn = queue.queueArn; diff --git a/packages/@aws-cdk/aws-sqs/lib/validate-props.ts b/packages/@aws-cdk/aws-sqs/lib/validate-props.ts index 45bf28894e084..5dafcdfe5a94e 100644 --- a/packages/@aws-cdk/aws-sqs/lib/validate-props.ts +++ b/packages/@aws-cdk/aws-sqs/lib/validate-props.ts @@ -2,11 +2,11 @@ import { toSeconds } from '@aws-cdk/cdk'; import { QueueProps } from './index'; export function validateProps(props: QueueProps) { - validateRange('delivery delay', toSeconds(props.deliveryDelay), 0, 900, 'seconds'); + validateRange('delivery delay', props.deliveryDelay && props.deliveryDelay.toSeconds(), 0, 900, 'seconds'); validateRange('maximum message size', props.maxMessageSizeBytes, 1_024, 262_144, 'bytes'); - validateRange('message retention period', toSeconds(props.retentionPeriod), 60, 1_209_600, 'seconds'); - validateRange('receive wait time', toSeconds(props.receiveMessageWaitTime), 0, 20, 'seconds'); - validateRange('visibility timeout', toSeconds(props.visibilityTimeout), 0, 43_200, 'seconds'); + validateRange('message retention period', props.retentionPeriod && props.retentionPeriod.toSeconds(), 60, 1_209_600, 'seconds'); + validateRange('receive wait time', props.receiveMessageWaitTime && props.receiveMessageWaitTime.toSeconds(), 0, 20, 'seconds'); + validateRange('visibility timeout', props.visibilityTimeout && props.visibilityTimeout.toSeconds(), 0, 43_200, 'seconds'); validateRange('dead letter target maximum receive count', props.deadLetterQueue && props.deadLetterQueue.maxReceiveCount, 1, +Infinity); } diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts index 87c3075371a10..c63a45823c77f 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/send-to-queue.ts @@ -60,7 +60,7 @@ export class SendToQueue implements sfn.IStepFunctionsTask { QueueUrl: this.queue.queueUrl, ...sfn.FieldUtils.renderObject({ MessageBody: this.props.messageBody.value, - DelaySeconds: toSeconds(this.props.delay), + DelaySeconds: this.props.delay && this.props.delay.toSeconds(), MessageDeduplicationId: this.props.messageDeduplicationId, MessageGroupId: this.props.messageGroupId, }) diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts index 2b333b91e3e43..edbabaddb9712 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts @@ -437,7 +437,7 @@ interface CatchTransition { function renderRetry(retry: RetryProps) { return { ErrorEquals: retry.errors, - IntervalSeconds: cdk.toSeconds(retry.interval), + IntervalSeconds: retry.interval && retry.interval.toSeconds(), MaxAttempts: retry.maxAttempts, BackoffRate: retry.backoffRate }; diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts index 10b41d3c7f37b..5e82f737d8218 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/task.ts @@ -128,8 +128,8 @@ export class Task extends State implements INextable { Resource: this.taskProps.resourceArn, Parameters: this.taskProps.parameters, ResultPath: renderJsonPath(this.resultPath), - TimeoutSeconds: cdk.toSeconds(this.timeout), - HeartbeatSeconds: cdk.toSeconds(this.taskProps.heartbeat), + TimeoutSeconds: this.timeout && this.timeout.toSeconds(), + HeartbeatSeconds: this.taskProps.heartbeat && this.taskProps.heartbeat.toSeconds(), }; } diff --git a/packages/@aws-cdk/cdk/lib/duration.ts b/packages/@aws-cdk/cdk/lib/duration.ts index 083d7a7627817..5802f899bf412 100644 --- a/packages/@aws-cdk/cdk/lib/duration.ts +++ b/packages/@aws-cdk/cdk/lib/duration.ts @@ -1,3 +1,5 @@ +import { Token } from "./token"; + /** * Represents a length of time. */ @@ -64,6 +66,9 @@ export class Duration { private readonly unit: TimeUnit; private constructor(amount: number, unit: TimeUnit) { + if (Token.isUnresolved(amount)) { + throw new Error(`Duration amounts cannot be unresolved tokens. Received ${amount}`); + } if (amount < 0) { throw new Error(`Duration amounts cannot be negative. Received: ${amount}`); } @@ -113,7 +118,12 @@ export class Duration { } public toString(): string { - return `${this.amount} ${this.unit}`; + return Token.asString( + () => { + throw new Error(`Duration.toString() was used, but .toSeconds, .toMinutes or .toDays should have been called instead`); + }, + { displayHint: `${this.amount} ${this.unit.label}` } + ); } private fractionDuration(symbol: string, modulus: number, next: (amount: number) => Duration): string { @@ -141,17 +151,6 @@ export interface TimeConversionOptions { readonly integral?: boolean; } -/** - * Helpful syntax sugar for turning an optional `Duration` into a count of seconds. - * - * @param duration an optional duration to convert into an amount of sunctions. - * - * @return `duration && duration.toSeconds()`. - */ -export function toSeconds(duration: Duration | undefined): number | undefined { - return duration && duration.toSeconds(); -} - class TimeUnit { public static readonly Seconds = new TimeUnit('seconds', 1); public static readonly Minutes = new TimeUnit('minutes', 60); diff --git a/packages/@aws-cdk/cdk/test/test.duration.ts b/packages/@aws-cdk/cdk/test/test.duration.ts index 8aeed3b83d1e0..817beae932cf2 100644 --- a/packages/@aws-cdk/cdk/test/test.duration.ts +++ b/packages/@aws-cdk/cdk/test/test.duration.ts @@ -1,5 +1,5 @@ import nodeunit = require('nodeunit'); -import { Duration } from '../lib'; +import { Duration, Stack, Token } from '../lib'; export = nodeunit.testCase({ 'negative amount'(test: nodeunit.Test) { @@ -8,6 +8,12 @@ export = nodeunit.testCase({ test.done(); }, + 'unresolved amount'(test: nodeunit.Test) { + test.throws(new Stack().resolve(Duration.seconds(Token.asNumber(1337)))); + + test.done(); + }, + 'Duration in seconds'(test: nodeunit.Test) { const duration = Duration.seconds(300); From f6d890a10faebc90f25a01bde9826d8c662752d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Jun 2019 14:36:51 +0200 Subject: [PATCH 4/5] Last feedback --- packages/@aws-cdk/cdk/lib/cfn-resource.ts | 5 +-- packages/@aws-cdk/cdk/lib/duration.ts | 40 ++++++++++++++------ packages/@aws-cdk/cdk/lib/resource-policy.ts | 4 +- packages/@aws-cdk/cdk/package.json | 3 +- packages/@aws-cdk/cdk/test/test.duration.ts | 7 +++- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/cdk/lib/cfn-resource.ts b/packages/@aws-cdk/cdk/lib/cfn-resource.ts index b343fbf402b6e..2f0fb721c8ba6 100644 --- a/packages/@aws-cdk/cdk/lib/cfn-resource.ts +++ b/packages/@aws-cdk/cdk/lib/cfn-resource.ts @@ -272,10 +272,7 @@ export class CfnResource extends CfnRefElement { if (!policy) { return undefined; } const result: any = { ...policy }; if (policy.resourceSignal && policy.resourceSignal.timeout) { - result.resourceSignal = { - ...policy.resourceSignal, - timeout: policy.resourceSignal.timeout.toISOString(), - }; + result.resourceSignal = policy.resourceSignal; } return result; } diff --git a/packages/@aws-cdk/cdk/lib/duration.ts b/packages/@aws-cdk/cdk/lib/duration.ts index 5802f899bf412..d9ba10c861373 100644 --- a/packages/@aws-cdk/cdk/lib/duration.ts +++ b/packages/@aws-cdk/cdk/lib/duration.ts @@ -2,6 +2,11 @@ import { Token } from "./token"; /** * Represents a length of time. + * + * The amount can be specified either as a literal value (e.g: `10`) which + * cannot be negative, or as an unresolved number token. + * + * Whent he amount is passed as an token, unit conversion is not possible. */ export class Duration { /** @@ -37,6 +42,8 @@ export class Duration { } /** + * Parse a period formatted according to the ISO 8601 standard (see https://www.iso.org/fr/standard/70907.html). + * * @param duration an ISO-formtted duration to be parsed. * @returns the parsed `Duration`. */ @@ -51,9 +58,9 @@ export class Duration { } return Duration.seconds( _toInt(seconds) - + (_toInt(minutes) * 60) - + (_toInt(hours) * 3_600) - + (_toInt(days) * 86_400) + + (_toInt(minutes) * TimeUnit.Minutes.inSeconds) + + (_toInt(hours) * TimeUnit.Hours.inSeconds) + + (_toInt(days) * TimeUnit.Days.inSeconds) ); function _toInt(str: string): number { @@ -66,10 +73,7 @@ export class Duration { private readonly unit: TimeUnit; private constructor(amount: number, unit: TimeUnit) { - if (Token.isUnresolved(amount)) { - throw new Error(`Duration amounts cannot be unresolved tokens. Received ${amount}`); - } - if (amount < 0) { + if (!Token.isUnresolved(amount) && amount < 0) { throw new Error(`Duration amounts cannot be negative. Received: ${amount}`); } @@ -81,25 +85,28 @@ export class Duration { * @returns the value of this `Duration` expressed in Seconds. */ public toSeconds(opts: TimeConversionOptions = {}): number { - return _ensureIntegral(this.amount * this.unit.inSeconds, opts); + if (this.unit === TimeUnit.Seconds) { return this.amount; } + return _ensureIntegral(this.amount, this.unit.inSeconds, opts); } /** * @returns the value of this `Duration` expressed in Minutes. */ public toMinutes(opts: TimeConversionOptions = {}): number { - return _ensureIntegral(this.amount * this.unit.inMinutes, opts); + if (this.unit === TimeUnit.Minutes) { return this.amount; } + return _ensureIntegral(this.amount, this.unit.inMinutes, opts); } /** * @returns the value of this `Duration` expressed in Days. */ public toDays(opts: TimeConversionOptions = {}): number { - return _ensureIntegral(this.amount * this.unit.inDays, opts); + if (this.unit === TimeUnit.Days) { return this.amount; } + return _ensureIntegral(this.amount, this.unit.inDays, opts); } /** - * @returns ISO representation of this period. + * @returns an ISO 8601 representation of this period (see https://www.iso.org/fr/standard/70907.html). */ public toISOString(): string { if (this.amount === 0) { return 'PT0S'; } @@ -117,6 +124,11 @@ export class Duration { } } + /** + * Returns a string representation of this `Duration` that is also a Token that cannot be successfully resolved. This + * protects users against inadvertently stringifying a `Duration` object, when they should have called one of the + * `to*` methods instead. + */ public toString(): string { return Token.asString( () => { @@ -170,7 +182,11 @@ class TimeUnit { } } -function _ensureIntegral(value: number, { integral = true }: TimeConversionOptions) { +function _ensureIntegral(amount: number, multiplier: number, { integral = true }: TimeConversionOptions): number { + if (Token.isUnresolved(amount)) { + throw new Error(`Unable to perform time unit conversion on un-resolved token ${amount}.`); + } + const value = amount * multiplier; if (!Number.isInteger(value) && integral) { throw new Error(`Required integral time unit conversion, but value ${value} is not integral.`); } diff --git a/packages/@aws-cdk/cdk/lib/resource-policy.ts b/packages/@aws-cdk/cdk/lib/resource-policy.ts index 20bc69c8f8d42..a3ff6b9d84691 100644 --- a/packages/@aws-cdk/cdk/lib/resource-policy.ts +++ b/packages/@aws-cdk/cdk/lib/resource-policy.ts @@ -1,5 +1,3 @@ -import { Duration } from "./duration"; - /** * Associate the CreationPolicy attribute with a resource to prevent its status from reaching create complete until * AWS CloudFormation receives a specified number of success signals or the timeout period is exceeded. To signal a @@ -63,7 +61,7 @@ export interface ResourceSignal { * The timeout period starts after AWS CloudFormation starts creating the resource, and the timeout expires no sooner * than the time you specify but can occur shortly thereafter. The maximum time that you can specify is 12 hours. */ - readonly timeout?: Duration; + readonly timeout?: string; } /** diff --git a/packages/@aws-cdk/cdk/package.json b/packages/@aws-cdk/cdk/package.json index afd66bd76b80b..04b2a465c89ac 100644 --- a/packages/@aws-cdk/cdk/package.json +++ b/packages/@aws-cdk/cdk/package.json @@ -36,6 +36,7 @@ "construct-ctor:@aws-cdk/cdk.App.", "construct-ctor:@aws-cdk/cdk.Root.", "construct-ctor:@aws-cdk/cdk.Stack..params*", + "duration-prop-type:@aws-cdk/cdk.ResourceSignal.timeout", "props-no-any:@aws-cdk/cdk.CfnOutputProps.value", "props-no-any:@aws-cdk/cdk.CfnParameterProps.default", "props-no-any:@aws-cdk/cdk.CfnResourceProps.properties", @@ -90,4 +91,4 @@ "node": ">= 8.10.0" }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/cdk/test/test.duration.ts b/packages/@aws-cdk/cdk/test/test.duration.ts index b7d438a707b45..a7c2a9c9b630d 100644 --- a/packages/@aws-cdk/cdk/test/test.duration.ts +++ b/packages/@aws-cdk/cdk/test/test.duration.ts @@ -9,9 +9,12 @@ export = nodeunit.testCase({ }, 'unresolved amount'(test: nodeunit.Test) { + const stack = new Stack(); + const lazyDuration = Duration.seconds(Token.asNumber({ resolve: () => 1337 })); + test.equals(stack.resolve(lazyDuration.toSeconds()), 1337); test.throws( - () => new Stack().resolve(Duration.seconds(Token.asNumber({ resolve: () => 1337 }))), - /Duration amounts cannot be unresolved tokens./ + () => stack.resolve(lazyDuration.toMinutes()), + /Unable to perform time unit conversion on un-resolved token/ ); test.done(); From 91dc9511054c59c551e2b07e4b39bcf8c7fe4bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Thu, 20 Jun 2019 14:38:23 +0200 Subject: [PATCH 5/5] Rename CloudTrail log retention --- packages/@aws-cdk/aws-cloudtrail/lib/index.ts | 4 ++-- packages/@aws-cdk/aws-cloudtrail/package.json | 6 ------ packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts index dc76d342c39a3..b2893e7c7b874 100644 --- a/packages/@aws-cdk/aws-cloudtrail/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudtrail/lib/index.ts @@ -67,7 +67,7 @@ export interface TrailProps { * * @default logs.RetentionDays.OneYear */ - readonly cloudWatchLogsRetentionTimeDays?: logs.RetentionDays; + readonly cloudWatchLogsRetention?: logs.RetentionDays; /** The AWS Key Management Service (AWS KMS) key ID that you want to use to encrypt CloudTrail logs. * @@ -151,7 +151,7 @@ export class Trail extends Resource { let logsRole: iam.IRole | undefined; if (props.sendToCloudWatchLogs) { logGroup = new logs.CfnLogGroup(this, "LogGroup", { - retentionInDays: props.cloudWatchLogsRetentionTimeDays || logs.RetentionDays.ONE_YEAR + retentionInDays: props.cloudWatchLogsRetention || logs.RetentionDays.ONE_YEAR }); logsRole = new iam.Role(this, 'LogsRole', { assumedBy: cloudTrailPrincipal }); diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index 2db2b332b0a1a..1003530de1d4a 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -88,12 +88,6 @@ "@aws-cdk/aws-s3": "^0.35.0", "@aws-cdk/cdk": "^0.35.0" }, - "awslint": { - "exclude": [ - "duration-prop-type:@aws-cdk/aws-cloudtrail.TrailProps.cloudWatchLogsRetentionTimeDays", - "duration-prop-name:@aws-cdk/aws-cloudtrail.TrailProps.cloudWatchLogsRetentionTimeDays" - ] - }, "engines": { "node": ">= 8.10.0" }, diff --git a/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts b/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts index 3955ebc2c0488..ab8e8f2f06f5b 100644 --- a/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts +++ b/packages/@aws-cdk/aws-cloudtrail/test/test.cloudtrail.ts @@ -106,7 +106,7 @@ export = { const stack = getTestStack(); new Trail(stack, 'MyAmazingCloudTrail', { sendToCloudWatchLogs: true, - cloudWatchLogsRetentionTimeDays: RetentionDays.ONE_WEEK + cloudWatchLogsRetention: RetentionDays.ONE_WEEK }); expect(stack).to(haveResource("AWS::CloudTrail::Trail"));