Skip to content

Commit

Permalink
add HealthCheck class
Browse files Browse the repository at this point in the history
changed comments

use bind
  • Loading branch information
go-to-k committed Sep 23, 2023
1 parent 629b131 commit d0cd255
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 161 deletions.
11 changes: 6 additions & 5 deletions packages/@aws-cdk/aws-apprunner-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,23 +216,24 @@ const service = new apprunner.Service(stack, 'Service', {
service.addSecret('LATER_SECRET', apprunner.Secret.fromSecretsManager(secret, 'field'));
```

## HealthCheck Configuration
## HealthCheck

To configure the health check for the service, use the `healthCheckConfiguration` attribute.
To configure the health check for the service, use the `healthCheck` attribute.

You can specify it by static methods `HealthCheck.http` or `HealthCheck.tcp`.

```ts
new apprunner.Service(this, 'Service', {
source: apprunner.Source.fromEcrPublic({
imageConfiguration: { port: 8000 },
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
}),
healthCheckConfiguration: {
healthCheck: apprunner.HealthCheck.http({
healthyThreshold: 5,
interval: Duration.seconds(10),
path: '/',
protocol: apprunner.HealthCheckProtocolType.HTTP,
timeout: Duration.seconds(10),
unhealthyThreshold: 10,
},
}),
});
```
165 changes: 98 additions & 67 deletions packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -710,9 +710,11 @@ export interface ServiceProps {
/**
* Settings for the health check that AWS App Runner performs to monitor the health of a service.
*
* You can specify it by static methods `HealthCheck.http` or `HealthCheck.tcp`.
*
* @default - no health check configuration
*/
readonly healthCheckConfiguration?: HealthCheckConfiguration;
readonly healthCheck?: HealthCheck;
}

/**
Expand Down Expand Up @@ -873,7 +875,7 @@ export enum HealthCheckProtocolType {
/**
* Describes the settings for the health check that AWS App Runner performs to monitor the health of a service.
*/
export interface HealthCheckConfiguration {
interface HealthCheckCommonOptions {
/**
* The number of consecutive checks that must succeed before App Runner decides that the service is healthy.
*
Expand All @@ -889,36 +891,111 @@ export interface HealthCheckConfiguration {
readonly interval?: cdk.Duration;

/**
* The URL that health check requests are sent to.
*
* `path` is only applicable when you set `protocol` to `HTTP`.
* The time, in seconds, to wait for a health check response before deciding it failed.
*
* @default /
* @default Duration.seconds(2)
*/
readonly path?: string;
readonly timeout?: cdk.Duration;

/**
* The IP protocol that App Runner uses to perform health checks for your service.
*
* If you set `protocol` to `HTTP`, App Runner sends health check requests to the HTTP path specified by `path`.
* The number of consecutive checks that must fail before App Runner decides that the service is unhealthy.
*
* @default HealthCheckProtocolType.TCP
* @default 5
*/
readonly protocol?: HealthCheckProtocolType;
readonly unhealthyThreshold?: number;
}

/**
* Properties used to define HTTP Based healthchecks.
*/
export interface HttpHealthCheckOptions extends HealthCheckCommonOptions {
/**
* The time, in seconds, to wait for a health check response before deciding it failed.
* The URL that health check requests are sent to.
*
* @default Duration.seconds(2)
* `path` is only applicable when you set `protocol` to `HTTP`.
*
* @default /
*/
readonly timeout?: cdk.Duration;
readonly path?: string;
}

/**
* Properties used to define TCP Based healthchecks.
*/
export interface TcpHealthCheckOptions extends HealthCheckCommonOptions { }

/**
* Contains static factory methods for creating health checks for different protocols
*/
export class HealthCheck {
/**
* Construct a HTTP health check
*/
public static http(options: HttpHealthCheckOptions = {}): HealthCheck {
return new HealthCheck(
HealthCheckProtocolType.HTTP,
options.healthyThreshold,
options.interval,
options.timeout,
options.unhealthyThreshold,
options.path,
);
}

/**
* The number of consecutive checks that must fail before App Runner decides that the service is unhealthy.
*
* @default 5
* Construct a TCP health check
*/
readonly unhealthyThreshold?: number;
public static tcp(options: TcpHealthCheckOptions = {}): HealthCheck {
return new HealthCheck(
HealthCheckProtocolType.TCP,
options.healthyThreshold,
options.interval,
options.timeout,
options.unhealthyThreshold,
);
}

private constructor(
public readonly healthCheckProtocolType: HealthCheckProtocolType,
public readonly healthyThreshold: number = 1,
public readonly interval: cdk.Duration = cdk.Duration.seconds(5),
public readonly timeout: cdk.Duration = cdk.Duration.seconds(2),
public readonly unhealthyThreshold: number = 5,
public readonly path?: string,
) {
if (this.healthCheckProtocolType === HealthCheckProtocolType.HTTP) {
if (this.path !== undefined && this.path.length === 0) {
throw new Error('path length must be greater than 0');
}
if (this.path === undefined) {
this.path = '/';
}
}

if (this.healthyThreshold < 1 || this.healthyThreshold > 20) {
throw new Error(`healthyThreshold must be between 1 and 20, got ${this.healthyThreshold}`);
}
if (this.unhealthyThreshold < 1 || this.unhealthyThreshold > 20) {
throw new Error(`unhealthyThreshold must be between 1 and 20, got ${this.unhealthyThreshold}`);
}
if (this.interval.toSeconds() < 1 || this.interval.toSeconds() > 20) {
throw new Error(`interval must be between 1 and 20 seconds, got ${this.interval.toSeconds()}`);
}
if (this.timeout.toSeconds() < 1 || this.timeout.toSeconds() > 20) {
throw new Error(`timeout must be between 1 and 20 seconds, got ${this.timeout.toSeconds()}`);
}
}

public bind(): CfnService.HealthCheckConfigurationProperty {
return {
healthyThreshold: this.healthyThreshold,
interval: this.interval?.toSeconds(),
path: this.path,
protocol: this.healthCheckProtocolType,
timeout: this.timeout?.toSeconds(),
unhealthyThreshold: this.unhealthyThreshold,
};
}
}

/**
Expand Down Expand Up @@ -1147,8 +1224,6 @@ export class Service extends cdk.Resource implements iam.IGrantable {
throw new Error('configurationValues cannot be provided if the ConfigurationSource is Repository');
}

this.validateHealthCheckConfiguration(this.props.healthCheckConfiguration);

const resource = new CfnService(this, 'Resource', {
serviceName: this.props.serviceName,
instanceConfiguration: {
Expand All @@ -1172,8 +1247,8 @@ export class Service extends cdk.Resource implements iam.IGrantable {
vpcConnectorArn: this.props.vpcConnector?.vpcConnectorArn,
},
},
healthCheckConfiguration: this.props.healthCheckConfiguration ?
this.renderHealthCheckConfiguration(this.props.healthCheckConfiguration) :
healthCheckConfiguration: this.props.healthCheck ?
this.props.healthCheck.bind() :
undefined,
});

Expand Down Expand Up @@ -1337,48 +1412,4 @@ export class Service extends cdk.Resource implements iam.IGrantable {
},
});
}

private renderHealthCheckConfiguration(props: HealthCheckConfiguration) {
return {
healthyThreshold: props.healthyThreshold,
interval: props.interval?.toSeconds(),
path: props.path,
protocol: props.protocol,
timeout: props.timeout?.toSeconds(),
unhealthyThreshold: props.unhealthyThreshold,
};
}

private validateHealthCheckConfiguration(healthCheckConfiguration?: HealthCheckConfiguration) {
if (!healthCheckConfiguration) return;

if (healthCheckConfiguration.path !== undefined) {
if (healthCheckConfiguration.protocol !== HealthCheckProtocolType.HTTP) {
throw new Error('path is only applicable when you set Protocol to HTTP');
}
if (healthCheckConfiguration.path.length === 0) {
throw new Error('path length must be greater than 0');
}
}
if (healthCheckConfiguration.healthyThreshold !== undefined) {
if (healthCheckConfiguration.healthyThreshold < 1 || healthCheckConfiguration.healthyThreshold > 20) {
throw new Error(`healthyThreshold must be between 1 and 20, got ${healthCheckConfiguration.healthyThreshold}`);
}
}
if (healthCheckConfiguration.unhealthyThreshold !== undefined) {
if (healthCheckConfiguration.unhealthyThreshold < 1 || healthCheckConfiguration.unhealthyThreshold > 20) {
throw new Error(`unhealthyThreshold must be between 1 and 20, got ${healthCheckConfiguration.unhealthyThreshold}`);
}
}
if (healthCheckConfiguration.interval !== undefined) {
if (healthCheckConfiguration.interval.toSeconds() < 1 || healthCheckConfiguration.interval.toSeconds() > 20) {
throw new Error(`interval must be between 1 and 20 seconds, got ${healthCheckConfiguration.interval.toSeconds()}`);
}
}
if (healthCheckConfiguration.timeout !== undefined) {
if (healthCheckConfiguration.timeout.toSeconds() < 1 || healthCheckConfiguration.timeout.toSeconds() > 20) {
throw new Error(`timeout must be between 1 and 20 seconds, got ${healthCheckConfiguration.timeout.toSeconds()}`);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as cdk from 'aws-cdk-lib';
import { HealthCheckProtocolType, Service, Source } from '../lib';
import { HealthCheck, Service, Source } from '../lib';
import { IntegTest } from '@aws-cdk/integ-tests-alpha';

const app = new cdk.App();
Expand All @@ -13,14 +13,13 @@ new Service(stack, 'Service', {
},
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
}),
healthCheckConfiguration: {
healthCheck: HealthCheck.http({
healthyThreshold: 5,
interval: cdk.Duration.seconds(10),
path: '/',
protocol: HealthCheckProtocolType.HTTP,
timeout: cdk.Duration.seconds(10),
unhealthyThreshold: 10,
},
}),
});

new IntegTest(app, 'cdk-integ-dashboard-and-widget-with-start-and-end', {
Expand Down
Loading

0 comments on commit d0cd255

Please sign in to comment.