diff --git a/packages/@aws-cdk/aws-apigateway/lib/stage.ts b/packages/@aws-cdk/aws-apigateway/lib/stage.ts index 6af6431c85759..9b9f56d27a7c0 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 } 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: options.cacheTtl && options.cacheTtl.toSeconds(), 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 67d99d6a13101..25231de473cdb 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -92,6 +92,7 @@ "awslint": { "exclude": [ "from-method:@aws-cdk/aws-apigateway.Resource", + "duration-prop-type:@aws-cdk/aws-apigateway.QuotaSettings.period", "from-method:@aws-cdk/aws-apigateway.ApiKey", "ref-via-interface:@aws-cdk/aws-apigateway.ApiKeyProps.resources", "props-physical-name:@aws-cdk/aws-apigateway.DeploymentProps", 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 94b5868a363c6..e9679d3af28a0 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: 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/step-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts index 3e354476637a0..85f2921110ebc 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, @@ -101,8 +101,9 @@ export class StepScalingPolicy extends cdk.Construct { } this.lowerAlarm = new cloudwatch.Alarm(this, 'LowerAlarm', { + // Recommended by AutoScaling metric: props.metric, - periodSec: 60, // Recommended by AutoScaling + period: cdk.Duration.minutes(1), // Recommended by AutoScaling alarmDescription: 'Lower threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD, evaluationPeriods: 1, @@ -116,7 +117,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, @@ -131,8 +132,9 @@ export class StepScalingPolicy extends cdk.Construct { } this.upperAlarm = new cloudwatch.Alarm(this, 'UpperAlarm', { + // Recommended by AutoScaling metric: props.metric, - periodSec: 60, // Recommended by AutoScaling + period: cdk.Duration.minutes(1), // Recommended by AutoScaling alarmDescription: 'Upper threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, 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 7e305ea6571a7..a6182c9701da7 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: props.scaleInCooldown && props.scaleInCooldown.toSeconds(), + scaleOutCooldown: props.scaleOutCooldown && props.scaleOutCooldown.toSeconds(), targetValue: props.targetValue } }); 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 7d837a925a28a..5a15dd4dfc9b0 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, Stack, Tag } from '@aws-cdk/cdk'; +import { AutoScalingRollingUpdate, Construct, Duration, Fn, IResource, Lazy, Resource, Stack, 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 @@ -364,10 +364,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 @@ -415,7 +411,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.toSeconds().toString() : undefined, minSize: minCapacity.toString(), maxSize: maxCapacity.toString(), desiredCapacity: desiredCapacity.toString(), @@ -542,12 +538,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, } }; } @@ -621,9 +617,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. @@ -667,14 +663,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/ @@ -683,31 +679,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 95ff67ffba3f6..e412af06acf45 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, PhysicalName, Resource } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, PhysicalName, Resource } 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. @@ -105,7 +105,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: props.heartbeatTimeout && props.heartbeatTimeout.toSeconds(), lifecycleHookName: this.physicalName.value, 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 38b4109dfd2d8..17cc3d0fe426c 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: props.estimatedInstanceWarmup && props.estimatedInstanceWarmup.toSeconds(), adjustmentType: props.adjustmentType, minAdjustmentMagnitude: props.minAdjustmentMagnitude, metricAggregationType: props.metricAggregationType, 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 854b296dd28a9..fe507ef052254 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, @@ -102,8 +102,9 @@ export class StepScalingPolicy extends cdk.Construct { } this.lowerAlarm = new cloudwatch.Alarm(this, 'LowerAlarm', { + // Recommended by AutoScaling metric: props.metric, - periodSec: 60, // Recommended by AutoScaling + period: cdk.Duration.minutes(1), // Recommended by AutoScaling alarmDescription: 'Lower threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD, evaluationPeriods: 1, @@ -117,7 +118,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, @@ -134,7 +135,7 @@ export class StepScalingPolicy extends cdk.Construct { this.upperAlarm = new cloudwatch.Alarm(this, 'UpperAlarm', { // Recommended by AutoScaling metric: props.metric, - periodSec: 60, // Recommended by AutoScaling + period: cdk.Duration.minutes(1), // Recommended by AutoScaling alarmDescription: 'Upper threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, 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 77ee46ad820c2..e50f516f86693 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.ALB_REQUEST_COUNT_PER_TARGET && !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: props.estimatedInstanceWarmup && props.estimatedInstanceWarmup.toSeconds(), 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 2e2780bfb276e..e41d936ea7b76 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 @@ -301,7 +301,7 @@ export = { updateType: autoscaling.UpdateType.ROLLING_UPDATE, rollingUpdateConfiguration: { minSuccessfulInstancesPercent: 50, - pauseTimeSec: 345 + pauseTime: cdk.Duration.seconds(345) } }); @@ -331,7 +331,7 @@ export = { machineImage: new ec2.AmazonLinuxImage(), vpc, resourceSignalCount: 5, - resourceSignalTimeoutSec: 666 + resourceSignalTimeout: cdk.Duration.seconds(666) }); // THEN @@ -386,7 +386,7 @@ export = { updateType: autoscaling.UpdateType.ROLLING_UPDATE, 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 0729a3f98bd20..876335bbca05d 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({ actions: ['acm:RequestCertificate', 'acm:DescribeCertificate', 'acm:DeleteCertificate'], 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 a46121b670ab4..be3f7af39aa57 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.resource.ts @@ -204,7 +204,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 3823553af8704..4e022fdc41ebe 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts @@ -194,9 +194,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. @@ -208,9 +208,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. @@ -300,7 +300,7 @@ export interface Behavior { * @default 86400 (1 day) * */ - readonly defaultTtlSeconds?: number; + readonly defaultTtl?: cdk.Duration; /** * The method this CloudFront distribution responds do. @@ -335,15 +335,15 @@ 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; /** * Declares associated lambda@edge functions for this distribution behaviour. @@ -389,25 +389,6 @@ export enum LambdaEdgeEventType { VIEWER_RESPONSE = "viewer-response", } -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 { /** @@ -625,8 +606,10 @@ 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: originConfig.customOriginSource.originKeepaliveTimeout + && originConfig.customOriginSource.originKeepaliveTimeout.toSeconds() || 5, + originReadTimeout: originConfig.customOriginSource.originReadTimeout + && originConfig.customOriginSource.originReadTimeout.toSeconds() || 30, originProtocolPolicy: originConfig.customOriginSource.originProtocolPolicy || OriginProtocolPolicy.HTTPS_ONLY, originSslProtocols: originConfig.customOriginSource.allowedOriginSSLVersions || [OriginSslPolicy.TLS_V1_2] } @@ -723,10 +706,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: input.defaultTtl && input.defaultTtl.toSeconds(), forwardedValues: input.forwardedValues || { queryString: false, cookies: { forward: "none" } }, - maxTtl: input.maxTtlSeconds, - minTtl: input.minTtlSeconds, + maxTtl: input.maxTtl && input.maxTtl.toSeconds(), + minTtl: input.minTtl && input.minTtl.toSeconds(), trustedSigners: input.trustedSigners, targetOriginId: input.targetOriginId, viewerProtocolPolicy: protoPolicy || ViewerProtocolPolicy.REDIRECT_TO_HTTPS, 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 e921a2c6c554d..1003530de1d4a 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -92,4 +92,4 @@ "node": ">= 8.10.0" }, "stability": "experimental" -} \ No newline at end of file +} 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")); diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts index 7ee032f5bf6bc..c17df4917a513 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 { ...dropUndef(config), ...dropUndef({ // Alarm overrides - period: props.periodSec, + period: props.period && props.period.toSeconds(), statistic: props.statistic && normalizeStatistic(props.statistic), }) }); diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts index 3f1f6bf26cf6c..6d0cc5f9a26fd 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts @@ -13,11 +13,9 @@ export interface CommonMetricOptions { /** * 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. @@ -110,23 +108,22 @@ export class Metric implements IMetric { 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; @@ -146,7 +143,7 @@ export class Metric implements IMetric { 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), @@ -164,7 +161,7 @@ export class Metric implements IMetric { return new Alarm(scope, id, { metric: this.with({ statistic: props.statistic, - periodSec: props.periodSec, + period: props.period, }), alarmName: props.alarmName, alarmDescription: props.alarmDescription, @@ -186,7 +183,7 @@ export class Metric implements IMetric { dimensions: dims.length > 0 ? dims : undefined, namespace: this.namespace, metricName: this.metricName, - period: this.periodSec, + period: this.period.toSeconds(), statistic: stat.type === 'simple' ? stat.statistic : undefined, extendedStatistic: stat.type === 'percentile' ? 'p' + stat.percentile : undefined, unit: this.unit @@ -198,7 +195,7 @@ export class Metric implements IMetric { dimensions: this.dimensionsAsList(), namespace: this.namespace, metricName: this.metricName, - period: this.periodSec, + period: this.period.toSeconds(), statistic: this.statistic, unit: this.unit, color: this.color, @@ -233,11 +230,9 @@ export interface CreateAlarmOptions { /** * 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/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index 289cdbfed58b8..f609d89253826 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -78,8 +78,14 @@ "@aws-cdk/aws-iam": "^0.35.0", "@aws-cdk/cdk": "^0.35.0" }, + "awslint": { + "exclude": [ + "duration-prop-type:@aws-cdk/aws-cloudwatch.MetricAlarmConfig.period", + "duration-prop-type:@aws-cdk/aws-cloudwatch.MetricGraphConfig.period" + ] + }, "engines": { "node": ">= 8.10.0" }, "stability": "experimental" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts index dfa9f4d51eaf7..e46b314eddd6a 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'; @@ -41,7 +41,7 @@ export = { // WHEN new Alarm(stack, 'Alarm', { metric: testMetric, - periodSec: 600, + period: Duration.minutes(10), threshold: 1000, evaluationPeriods: 3, }); @@ -147,7 +147,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 e6993fdfdb694..2bc55fcd51d28 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -5,7 +5,7 @@ import { DockerImageAsset, DockerImageAssetProps } from '@aws-cdk/aws-ecr-assets import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); -import { Aws, CfnResource, Construct, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Aws, CfnResource, Construct, Duration, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { IArtifacts } from './artifacts'; import { BuildSpec } from './build-spec'; import { Cache } from './cache'; @@ -448,9 +448,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. @@ -692,7 +692,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: sourceConfig.buildTriggers, diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 0160f7f4370a7..141e0620d4750 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -110,7 +110,9 @@ }, "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" diff --git a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts index 7d8c6ded5aec5..467ee5adb43ea 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -782,7 +782,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 71dbfd50fe14e..343fdb9553162 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 532b49c60ee79..8161363dd40aa 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 c3d13c305bf1a..2d6c504d584d8 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-assets/lib/adopted-repository.ts b/packages/@aws-cdk/aws-ecr-assets/lib/adopted-repository.ts index 258af7ac16c64..13e417d40d703 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/adopted-repository.ts +++ b/packages/@aws-cdk/aws-ecr-assets/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-ecr-assets/package-lock.json b/packages/@aws-cdk/aws-ecr-assets/package-lock.json index 19a96836c6102..3c27edb8bdf25 100644 --- a/packages/@aws-cdk/aws-ecr-assets/package-lock.json +++ b/packages/@aws-cdk/aws-ecr-assets/package-lock.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-ecr-assets", - "version": "0.34.0", + "version": "0.35.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/@aws-cdk/aws-ecr/lib/lifecycle.ts b/packages/@aws-cdk/aws-ecr/lib/lifecycle.ts index 74d688c1285fd..63468e4f1ae69 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 7da17a6686a9d..ead71e012c318 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -391,8 +391,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) { @@ -476,9 +476,9 @@ function renderLifecycleRule(rule: LifecycleRule) { selection: { tagStatus: rule.tagStatus || TagStatus.ANY, tagPrefixList: rule.tagPrefixList, - countType: rule.maxImageAgeDays !== undefined ? CountType.SINCE_IMAGE_PUSHED : CountType.IMAGE_COUNT_MORE_THAN, - countNumber: rule.maxImageAgeDays !== undefined ? rule.maxImageAgeDays : rule.maxImageCount, - countUnit: rule.maxImageAgeDays !== undefined ? 'days' : undefined, + countType: rule.maxImageAge !== undefined ? CountType.SINCE_IMAGE_PUSHED : CountType.IMAGE_COUNT_MORE_THAN, + 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 aebe2c44b317e..b0bf0e7a39365 100644 --- a/packages/@aws-cdk/aws-ecr/test/test.repository.ts +++ b/packages/@aws-cdk/aws-ecr/test/test.repository.ts @@ -54,7 +54,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 969adcce23f0f..808b8674e2fe5 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, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; -import cdk = require('@aws-cdk/cdk'); +import { Construct, Duration, Fn, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } 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, @@ -153,7 +152,7 @@ export abstract class BaseService extends Resource maximumPercent: props.maximumPercent || 200, minimumHealthyPercent: props.minimumHealthyPercent === undefined ? 50 : props.minimumHealthyPercent }, - healthCheckGracePeriodSeconds: props.healthCheckGracePeriodSeconds, + 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 }), @@ -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; const serviceName = longArnEnabled - ? cdk.Fn.select(2, cdk.Fn.split('/', this.resource.refAsString)) + ? Fn.select(2, Fn.split('/', this.resource.refAsString)) : this.resource.attrName; const resourceIdentifiers = new ResourceIdentifiers(this, { @@ -423,7 +422,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 f983469152860..b9164b3b964e4 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 7bc08dfd44f95..a3917d4443460 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -4,7 +4,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import cloudmap = require('@aws-cdk/aws-servicediscovery'); import ssm = require('@aws-cdk/aws-ssm'); -import { Construct, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { InstanceDrainHook } from './drain-hook/instance-drain-hook'; import { CfnCluster } from './ecs.generated'; @@ -185,11 +185,11 @@ export class Cluster extends Resource implements ICluster { })); // 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 }); } } @@ -448,9 +448,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 5f0d6facaa517..2342a78fc5fab 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -436,9 +436,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. @@ -456,16 +456,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 { @@ -479,10 +479,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: hc.startPeriod && hc.startPeriod.toSeconds(), + 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 d3a8c8b55577c..18d036c22a858 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.INSTANCE_TERMINATING, defaultResult: autoscaling.DefaultResult.CONTINUE, notificationTarget: new hooks.FunctionHook(fn), - heartbeatTimeoutSec: drainTimeSeconds, + heartbeatTimeout: drainTime, }); // Describe actions cannot be restricted and restrict the CompleteLifecycleAction to the ASG arn 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 9c94f43dd0be2..2657abec6a633 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 @@ -1031,7 +1031,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 18a1c6f58d888..6ec76a524c808 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 @@ -171,7 +171,7 @@ export = { new ecs.FargateService(stack, 'Svc', { cluster, taskDefinition, - healthCheckGracePeriodSeconds: 10 + healthCheckGracePeriod: cdk.Duration.seconds(10) }); // THEN @@ -490,7 +490,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 9083c83276ecc..f1f5b41998ea7 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.ONE_MONTH, + logRetention: logs.RetentionDays.ONE_MONTH, multilinePattern: 'pattern', streamPrefix: 'hello' }); @@ -74,7 +74,7 @@ export = { // THEN test.throws(() => new ecs.AwsLogDriver(stack, 'Log', { logGroup, - logRetentionDays: logs.RetentionDays.FIVE_DAYS, + logRetention: logs.RetentionDays.FIVE_DAYS, 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 c75f5aa7e8285..ccf9a0ee2c78d 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 fd33454bab454..d5c27afcddea7 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'; /** @@ -106,16 +106,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; } /** @@ -422,9 +422,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 63468e3938d34..08f9f3428910f 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 0d2d5e4a323f2..be1042c3eeaf2 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, Token } from '@aws-cdk/cdk'; +import { Construct, Duration, Lazy, Resource, Stack, Token } 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 af25fbd28c211..b5c808029fbca 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 dd54758b40ddf..8569931dc0508 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 @@ -84,7 +84,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 902efb7ee5ed3..d9df7d354e1dc 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', () => { schedule: events.Schedule.rate(1, events.TimeUnit.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 0a51516ee4252..0fab6b8130c0d 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, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; import { Grant } from './grant'; import { CfnRole } from './iam.generated'; import { IIdentity } from './identity-base'; @@ -73,9 +73,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 @@ -90,9 +89,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; } /** @@ -207,7 +206,8 @@ export class Role extends Resource implements IRole { this.assumeRolePolicy = createAssumeRolePolicy(props.assumedBy, props.externalId); this.managedPolicies.push(...props.managedPolicies || []); - validateMaxSessionDuration(props.maxSessionDurationSec); + const maxSessionDuration = props.maxSessionDuration && props.maxSessionDuration.toSeconds(); + validateMaxSessionDuration(maxSessionDuration); const role = new CfnRole(this, 'Resource', { assumeRolePolicyDocument: this.assumeRolePolicy as any, @@ -215,7 +215,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.attrRoleId; diff --git a/packages/@aws-cdk/aws-iam/test/test.role.ts b/packages/@aws-cdk/aws-iam/test/test.role.ts index 93d423b653200..0191a841825f0 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 0d43a451e565c..c86697cce6ba9 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, PhysicalName, ResourceIdentifiers, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, Duration, Fn, Lazy, PhysicalName, ResourceIdentifiers, Stack, Token } 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; } /** @@ -424,7 +424,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: props.timeout && props.timeout.toSeconds(), runtime: props.runtime.name, role: this.role.roleArn, environment: Lazy.anyValue({ produce: () => this.renderEnvironment() }), @@ -463,10 +463,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 }); } } @@ -597,7 +597,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 bd4d3ab20ebc0..0e49857094f96 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 a994520a8bd7c..c9ada091a6b14 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.ONE_WEEK + logRetention: logs.RetentionDays.ONE_WEEK }); 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.ONE_MONTH + logRetention: logs.RetentionDays.ONE_MONTH }); 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.ONE_YEAR + logRetention: logs.RetentionDays.ONE_YEAR }); 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 e5daf941aa100..9a81fd8a69c34 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: cdk.PhysicalName.of('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: cdk.PhysicalName.of('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: cdk.PhysicalName.of('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.ONE_MONTH + logRetention: logs.RetentionDays.ONE_MONTH }); // 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 3a514bff744f7..94fe1791b9303 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 LogRetention(stack, 'MyLambda', { logGroupName: 'group', - retentionDays: logs.RetentionDays.ONE_MONTH + retention: logs.RetentionDays.ONE_MONTH }); // 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 2fae3647099b7..12183f1a00601 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -278,9 +278,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; /** * Determine the removal policy of this log group. @@ -326,7 +326,7 @@ export class LogGroup extends LogGroupBase { physicalName: props.logGroupName, }); - let retentionInDays = props.retentionDays; + let retentionInDays = props.retention; if (retentionInDays === undefined) { retentionInDays = RetentionDays.TWO_YEARS; } if (retentionInDays === Infinity) { retentionInDays = undefined; } diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 4f09fa9f9925f..8187007490984 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -91,4 +91,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 69b8b8a39f813..230eec2e165a5 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.ONE_WEEK + retention: RetentionDays.ONE_WEEK }); /// !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 947d50e2e4268..8fa9c604c12f1 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.ONE_WEEK + retention: RetentionDays.ONE_WEEK }); // 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, removalPolicy: RemovalPolicy.Destroy }); diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 9339efffff502..3f108534ff012 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 d5cbcd66d6af5..23bc571c222f2 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, IResource, RemovalPolicy, Resource, SecretValue, Stack, Token } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, RemovalPolicy, Resource, SecretValue, Stack, Token } 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. @@ -501,7 +501,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData this.newCfnProps = { autoMinorVersionUpgrade: props.autoMinorVersionUpgrade, availabilityZone: props.multiAz ? undefined : props.availabilityZone, - backupRetentionPeriod: props.backupRetentionPeriod !== undefined ? props.backupRetentionPeriod : undefined, + backupRetentionPeriod: props.backupRetention ? props.backupRetention.toDays() : undefined, copyTagsToSnapshot: props.copyTagsToSnapshot !== undefined ? props.copyTagsToSnapshot : true, dbInstanceClass: `db.${props.instanceClass}`, dbInstanceIdentifier: props.instanceIdentifier, @@ -512,7 +512,7 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData enableIamDatabaseAuthentication: props.iamAuthentication, enablePerformanceInsights: props.enablePerformanceInsights, iops, - monitoringInterval: props.monitoringInterval, + monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(), monitoringRoleArn: monitoringRole && monitoringRole.roleArn, multiAz: props.multiAz, optionGroupName: props.optionGroup && props.optionGroup.optionGroupName, @@ -520,7 +520,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, @@ -537,7 +537,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 9cdc4a129362b..b24891fd80da3 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 4cc35da44ca7a..e44c343078835 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.minutes(1), enablePerformanceInsights: true, cloudwatchLogsExports: [ 'trace', @@ -430,7 +430,7 @@ export = { dimensions: { DBInstanceIdentifier: { Ref: 'InstanceC1063A87' } }, namespace: 'AWS/RDS', metricName: 'CPUUtilization', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Average' }); @@ -479,7 +479,7 @@ export = { instanceClass: new ec2.InstanceTypePair(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), masterUsername: 'admin', vpc, - backupRetentionPeriod: 0, + backupRetention: cdk.Duration.seconds(0), }); // THEN diff --git a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts index 87d2f346b103e..31fed6c27f41d 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, ContextProvider, Lazy, Resource } from '@aws-cdk/cdk'; +import { Construct, ContextProvider, Duration, Lazy, Resource } from '@aws-cdk/cdk'; import cxapi = require('@aws-cdk/cx-api'); import { HostedZoneProviderProps } from './hosted-zone-provider'; import { HostedZoneAttributes, IHostedZone } from './hosted-zone-ref'; @@ -217,7 +217,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..24d98f0f1b219 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 } 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 : ((props.ttl && props.ttl.toSeconds()) || 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-assets/package-lock.json b/packages/@aws-cdk/aws-s3-assets/package-lock.json index 7c14726c9ec0f..ccc03d6cf433b 100644 --- a/packages/@aws-cdk/aws-s3-assets/package-lock.json +++ b/packages/@aws-cdk/aws-s3-assets/package-lock.json @@ -1,6 +1,6 @@ { "name": "@aws-cdk/aws-s3-assets", - "version": "0.34.0", + "version": "0.35.0", "lockfileVersion": 1, "requires": true, "dependencies": { 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 4514758cbe124..f6c13cfc3fd70 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 fb79be37d2713..ca8764558b19b 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -951,7 +951,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"); @@ -1148,21 +1148,21 @@ export class Bucket extends BucketBase { 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, + noncurrentVersionExpirationInDays: rule.noncurrentVersionExpiration && rule.noncurrentVersionExpiration.toDays(), noncurrentVersionTransitions: mapOrUndefined(rule.noncurrentVersionTransitions, t => ({ storageClass: t.storageClass.value, - transitionInDays: t.transitionInDays + transitionInDays: t.transitionAfter.toDays() })), prefix: rule.prefix, status: enabled ? 'Enabled' : 'Disabled', transitions: mapOrUndefined(rule.transitions, t => ({ storageClass: t.storageClass.value, transitionDate: t.transitionDate, - transitionInDays: t.transitionInDays + transitionInDays: t.transitionAfter && t.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 ea20482e63ee9..e2f976e264e7a 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 02a1e76a7f991..e17bc7bf98ede 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 87c2b09c2eec0..964054033b7d7 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 0c727f0cf6769..160cde6ca49fe 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -213,7 +213,7 @@ export = { dimensions: {TopicName: { 'Fn::GetAtt': [ 'TopicBFC7AF6E', 'TopicName' ] }}, namespace: 'AWS/SNS', metricName: 'NumberOfMessagesPublished', - periodSec: 300, + period: cdk.Duration.minutes(5), statistic: 'Sum' }); @@ -221,7 +221,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 a92a0f95c045f..a3ad0d3c05167 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, PhysicalName, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, PhysicalName, ResourceIdentifiers, Stack } 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. @@ -250,11 +250,11 @@ export class Queue extends QueueBase { ...this.determineFifoProps(props), ...encryptionProps, redrivePolicy, - delaySeconds: props.deliveryDelaySec, + delaySeconds: props.deliveryDelay && props.deliveryDelay.toSeconds(), maximumMessageSize: props.maxMessageSizeBytes, - messageRetentionPeriod: props.retentionPeriodSec, - receiveMessageWaitTimeSeconds: props.receiveMessageWaitTimeSec, - visibilityTimeout: props.visibilityTimeoutSec, + messageRetentionPeriod: props.retentionPeriod && props.retentionPeriod.toSeconds(), + receiveMessageWaitTimeSeconds: props.receiveMessageWaitTime && props.receiveMessageWaitTime.toSeconds(), + visibilityTimeout: props.visibilityTimeout && props.visibilityTimeout.toSeconds(), }); const resourceIdentifiers = new ResourceIdentifiers(this, { @@ -288,7 +288,7 @@ export class Queue extends QueueBase { encryptionMasterKey: masterKey, encryptionProps: { kmsMasterKeyId: 'alias/aws/sqs', - kmsDataKeyReusePeriodSeconds: props.dataKeyReuseSec + kmsDataKeyReusePeriodSeconds: props.dataKeyReuse && props.dataKeyReuse.toSeconds() } }; } @@ -302,7 +302,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..3d7781fabd957 100644 --- a/packages/@aws-cdk/aws-sqs/lib/validate-props.ts +++ b/packages/@aws-cdk/aws-sqs/lib/validate-props.ts @@ -1,11 +1,11 @@ import { QueueProps } from './index'; export function validateProps(props: QueueProps) { - validateRange('delivery delay', props.deliveryDelaySec, 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', 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', 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-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index 711eacc105fd2..90650b51226d5 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'; @@ -286,7 +286,7 @@ export = { dimensions: {QueueName: { 'Fn::GetAtt': [ 'Queue4A7E3555', 'QueueName' ] }}, namespace: 'AWS/SQS', metricName: 'NumberOfMessagesSent', - periodSec: 300, + period: Duration.minutes(5), statistic: 'Sum' }); @@ -294,7 +294,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/sagemaker-task-base-types.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-task-base-types.ts index b202072e98502..9648b4bac4928 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-task-base-types.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-task-base-types.ts @@ -1,5 +1,6 @@ import ec2 = require('@aws-cdk/aws-ec2'); import kms = require('@aws-cdk/aws-kms'); +import { Duration } from '@aws-cdk/cdk'; // // Create Training Job types @@ -146,7 +147,7 @@ export interface StoppingCondition { /** * The maximum length of time, in seconds, that the training or compilation job can run. */ - readonly maxRuntimeInSeconds?: number; + readonly maxRuntime?: Duration; } export interface ResourceConfig { @@ -470,4 +471,4 @@ export enum AssembleWith { */ LINE = 'Line' -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-train-task.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-train-task.ts index 62258906ed390..7f649027e9a9d 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-train-task.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/sagemaker-train-task.ts @@ -1,7 +1,7 @@ import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); import sfn = require('@aws-cdk/aws-stepfunctions'); -import { Construct, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, Stack } from '@aws-cdk/cdk'; import { AlgorithmSpecification, Channel, InputMode, OutputDataConfig, ResourceConfig, S3DataType, StoppingCondition, VpcConfig, } from './sagemaker-task-base-types'; @@ -116,7 +116,7 @@ export class SagemakerTrainTask implements ec2.IConnectable, sfn.IStepFunctionsT // set the stopping condition if not defined this.stoppingCondition = props.stoppingCondition || { - maxRuntimeInSeconds: 3600 + maxRuntime: Duration.hours(1) }; // set the sagemaker role or create new one @@ -229,7 +229,7 @@ export class SagemakerTrainTask implements ec2.IConnectable, sfn.IStepFunctionsT private renderStoppingCondition(config: StoppingCondition): {[key: string]: any} { return { StoppingCondition: { - MaxRuntimeInSeconds: config.maxRuntimeInSeconds + MaxRuntimeInSeconds: config.maxRuntime && config.maxRuntime.toSeconds() } }; } @@ -290,4 +290,4 @@ export class SagemakerTrainTask implements ec2.IConnectable, sfn.IStepFunctionsT return policyStatements; } -} \ No newline at end of file +} 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 3a202c5962e0d..ac4986ed69b6a 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 } 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. @@ -74,7 +75,7 @@ export class SendToQueue implements sfn.IStepFunctionsTask { QueueUrl: this.queue.queueUrl, ...sfn.FieldUtils.renderObject({ MessageBody: this.props.messageBody.value, - DelaySeconds: this.props.delaySeconds, + DelaySeconds: this.props.delay && this.props.delay.toSeconds(), MessageDeduplicationId: this.props.messageDeduplicationId, MessageGroupId: this.props.messageGroupId, }) diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.ts index 2f0e1d2a797ec..0f3a4397c7fb4 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.invoke-function.ts @@ -51,7 +51,7 @@ const chain = sfn.Chain new sfn.StateMachine(stack, 'StateMachine', { definition: chain, - timeoutSec: 30 + timeout: cdk.Duration.seconds(30) }); -app.run(); \ No newline at end of file +app.run(); 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-tasks/test/sagemaker-training-job.test.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker-training-job.test.ts index 345b369f6e0ff..b0840bd8b5e16 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker-training-job.test.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/sagemaker-training-job.test.ts @@ -140,7 +140,7 @@ test('create complex training job', () => { volumeKmsKeyId: kmsKey, }, stoppingCondition: { - maxRuntimeInSeconds: 3600 + maxRuntime: cdk.Duration.hours(1) }, tags: { Project: "MyProject" @@ -259,7 +259,7 @@ test('pass param to training job', () => { volumeSizeInGB: 50 }, stoppingCondition: { - maxRuntimeInSeconds: 3600 + maxRuntime: cdk.Duration.hours(1) } })}); @@ -299,4 +299,4 @@ test('pass param to training job', () => { } }, }); -}); \ No newline at end of file +}); diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md index 603971a58e6ee..ad7636cf84d26 100644 --- a/packages/@aws-cdk/aws-stepfunctions/README.md +++ b/packages/@aws-cdk/aws-stepfunctions/README.md @@ -73,7 +73,7 @@ const definition = submitJob new sfn.StateMachine(this, 'StateMachine', { definition, - timeoutSec: 300 + timeout: Duration.minutes(5) }); ``` @@ -148,12 +148,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 04d0f8029caa0..61830a23ff6eb 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, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk'; +import { Construct, Duration, IResource, PhysicalName, Resource, ResourceIdentifiers, 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; } /** @@ -96,7 +96,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: this.physicalName.value, diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts index 5dce6925b53d7..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: retry.intervalSeconds, + IntervalSeconds: retry.interval && retry.interval.toSeconds(), 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..5e82f737d8218 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: this.timeout && this.timeout.toSeconds(), + HeartbeatSeconds: this.taskProps.heartbeat && this.taskProps.heartbeat.toSeconds(), }; } 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 7db195bfca578..a357bb7ef1cd0 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 3fff5bc52d9ad..2f0fb721c8ba6 100644 --- a/packages/@aws-cdk/cdk/lib/cfn-resource.ts +++ b/packages/@aws-cdk/cdk/lib/cfn-resource.ts @@ -231,7 +231,7 @@ export class CfnResource extends CfnRefElement { Type: this.cfnResourceType, Properties: ignoreEmpty(this.cfnProperties), 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), @@ -267,6 +267,15 @@ 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; + } + return result; + } } protected get cfnProperties(): { [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..d9ba10c861373 --- /dev/null +++ b/packages/@aws-cdk/cdk/lib/duration.ts @@ -0,0 +1,194 @@ +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 { + /** + * @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); + } + + /** + * 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`. + */ + 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) * TimeUnit.Minutes.inSeconds) + + (_toInt(hours) * TimeUnit.Hours.inSeconds) + + (_toInt(days) * TimeUnit.Days.inSeconds) + ); + + 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 (!Token.isUnresolved(amount) && 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 { + 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 { + 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 { + if (this.unit === TimeUnit.Days) { return this.amount; } + return _ensureIntegral(this.amount, this.unit.inDays, opts); + } + + /** + * @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'; } + 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}`); + } + } + + /** + * 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( + () => { + 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 { + 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; +} + +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(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.`); + } + return value; +} diff --git a/packages/@aws-cdk/cdk/lib/index.ts b/packages/@aws-cdk/cdk/lib/index.ts index a624d4cdf9892..5fbcd44156cf3 100644 --- a/packages/@aws-cdk/cdk/lib/index.ts +++ b/packages/@aws-cdk/cdk/lib/index.ts @@ -26,6 +26,7 @@ export * from './cfn-dynamic-reference'; export * from './tag'; export * from './removal-policy'; export * from './arn'; +export * from './duration'; export * from './stack-trace'; export * from './app'; 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 new file mode 100644 index 0000000000000..a7c2a9c9b630d --- /dev/null +++ b/packages/@aws-cdk/cdk/test/test.duration.ts @@ -0,0 +1,111 @@ +import nodeunit = require('nodeunit'); +import { Duration, Stack, Token } from '../lib'; + +export = nodeunit.testCase({ + 'negative amount'(test: nodeunit.Test) { + test.throws(() => Duration.seconds(-1), /negative/); + + test.done(); + }, + + '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( + () => stack.resolve(lazyDuration.toMinutes()), + /Unable to perform time unit conversion on un-resolved token/ + ); + + 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(), /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); + + 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(), /Required integral time unit conversion, but value/); + floatEqual(test, 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(), /Required integral time unit conversion, but value/); + floatEqual(test, 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(); + } +}); + +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}`, + ); +} 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';