Skip to content

Commit

Permalink
feat(core): Introduced Duration class (#2857)
Browse files Browse the repository at this point in the history
This can be used to model durations in a user-friendly way, while
allowing coercion into the desired/required time unit at the usage site.

There is now a default requirement that properties exposed by the APIs
and that have a duration-like name (ending in `duration`, `period`,
`timeout` or `ttl`) have a type of `@aws-cdk/cdk.Duration` and do not
contain a unit suffix (`Days`, `Millis`, `Seconds`, `Sec`, ...).

BREAKING CHANGE: Properties throughout the AWS Construct Libraries that
                 represent lengths of time have been re-typed to be
                 `@aws-cdk/cdk.Duration` instead of `number`, and were
                 renamed to exclude any unit indication.
  • Loading branch information
RomainMuller committed Jun 20, 2019
1 parent 4f13e18 commit 2ceec6c
Show file tree
Hide file tree
Showing 118 changed files with 820 additions and 492 deletions.
10 changes: 5 additions & 5 deletions packages/@aws-cdk/aws-apigateway/lib/stage.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
};

Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-apigateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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', {
Expand All @@ -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
}
});
Expand Down
53 changes: 12 additions & 41 deletions packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
}
};
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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/
Expand All @@ -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}`);
Expand Down
6 changes: 3 additions & 3 deletions packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 7 additions & 6 deletions packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 2ceec6c

Please sign in to comment.