Skip to content

Commit

Permalink
add state machine support (#78)
Browse files Browse the repository at this point in the history
* add state machine support

* reorder how things are added
  • Loading branch information
mark-ship-it authored Oct 28, 2021
1 parent 085636c commit 238e2af
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 1 deletion.
10 changes: 10 additions & 0 deletions .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .projenrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const project = new AwsCdkConstructLibrary({
'@aws-cdk/aws-sns',
'@aws-cdk/aws-sns-subscriptions',
'@aws-cdk/aws-sqs',
'@aws-cdk/aws-stepfunctions',
'@aws-cdk/core',
],

Expand Down
121 changes: 121 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,30 @@ public watchSqs(title: string, sqs: IQueue, options?: WatchSqsOptions)

---

##### `watchStateMachine` <a name="@myhelix/cdk-watchful.Watchful.watchStateMachine"></a>

```typescript
public watchStateMachine(title: string, stateMachine: StateMachine, options?: WatchStateMachineOptions)
```

###### `title`<sup>Required</sup> <a name="@myhelix/cdk-watchful.Watchful.parameter.title"></a>

- *Type:* `string`

---

###### `stateMachine`<sup>Required</sup> <a name="@myhelix/cdk-watchful.Watchful.parameter.stateMachine"></a>

- *Type:* [`@aws-cdk/aws-stepfunctions.StateMachine`](#@aws-cdk/aws-stepfunctions.StateMachine)

---

###### `options`<sup>Optional</sup> <a name="@myhelix/cdk-watchful.Watchful.parameter.options"></a>

- *Type:* [`@myhelix/cdk-watchful.WatchStateMachineOptions`](#@myhelix/cdk-watchful.WatchStateMachineOptions)

---




Expand Down Expand Up @@ -550,6 +574,38 @@ new WatchSqsService(scope: Construct, id: string, props: WatchSqsServiceProps)



### WatchStateMachine <a name="@myhelix/cdk-watchful.WatchStateMachine"></a>

#### Initializer <a name="@myhelix/cdk-watchful.WatchStateMachine.Initializer"></a>

```typescript
import { WatchStateMachine } from '@myhelix/cdk-watchful'

new WatchStateMachine(scope: Construct, id: string, props: WatchStateMachineProps)
```

##### `scope`<sup>Required</sup> <a name="@myhelix/cdk-watchful.WatchStateMachine.parameter.scope"></a>

- *Type:* [`@aws-cdk/core.Construct`](#@aws-cdk/core.Construct)

---

##### `id`<sup>Required</sup> <a name="@myhelix/cdk-watchful.WatchStateMachine.parameter.id"></a>

- *Type:* `string`

---

##### `props`<sup>Required</sup> <a name="@myhelix/cdk-watchful.WatchStateMachine.parameter.props"></a>

- *Type:* [`@myhelix/cdk-watchful.WatchStateMachineProps`](#@myhelix/cdk-watchful.WatchStateMachineProps)

---





## Structs <a name="Structs"></a>

### QuickLink <a name="@myhelix/cdk-watchful.QuickLink"></a>
Expand Down Expand Up @@ -1071,6 +1127,15 @@ Automatically watch RDS Aurora clusters in the scope.

---

##### `stateMachine`<sup>Optional</sup> <a name="@myhelix/cdk-watchful.WatchfulAspectProps.property.stateMachine"></a>

- *Type:* `boolean`
- *Default:* true

Automatically watch AWS state machines in the scope.

---

### WatchfulProps <a name="@myhelix/cdk-watchful.WatchfulProps"></a>

#### Initializer <a name="[object Object].Initializer"></a>
Expand Down Expand Up @@ -1473,6 +1538,62 @@ const watchSqsServiceProps: WatchSqsServiceProps = { ... }

---

### WatchStateMachineOptions <a name="@myhelix/cdk-watchful.WatchStateMachineOptions"></a>

#### Initializer <a name="[object Object].Initializer"></a>

```typescript
import { WatchStateMachineOptions } from '@myhelix/cdk-watchful'

const watchStateMachineOptions: WatchStateMachineOptions = { ... }
```

##### `metricFailedThreshold`<sup>Optional</sup> <a name="@myhelix/cdk-watchful.WatchStateMachineOptions.property.metricFailedThreshold"></a>

- *Type:* `number`
- *Default:* 1 any execution failure will trigger the alarm

Alarm when execution failures reach this threshold over 1 minute.

---

### WatchStateMachineProps <a name="@myhelix/cdk-watchful.WatchStateMachineProps"></a>

#### Initializer <a name="[object Object].Initializer"></a>

```typescript
import { WatchStateMachineProps } from '@myhelix/cdk-watchful'

const watchStateMachineProps: WatchStateMachineProps = { ... }
```

##### `metricFailedThreshold`<sup>Optional</sup> <a name="@myhelix/cdk-watchful.WatchStateMachineProps.property.metricFailedThreshold"></a>

- *Type:* `number`
- *Default:* 1 any execution failure will trigger the alarm

Alarm when execution failures reach this threshold over 1 minute.

---

##### `stateMachine`<sup>Required</sup> <a name="@myhelix/cdk-watchful.WatchStateMachineProps.property.stateMachine"></a>

- *Type:* [`@aws-cdk/aws-stepfunctions.StateMachine`](#@aws-cdk/aws-stepfunctions.StateMachine)

---

##### `title`<sup>Required</sup> <a name="@myhelix/cdk-watchful.WatchStateMachineProps.property.title"></a>

- *Type:* `string`

---

##### `watchful`<sup>Required</sup> <a name="@myhelix/cdk-watchful.WatchStateMachineProps.property.watchful"></a>

- *Type:* [`@myhelix/cdk-watchful.IWatchful`](#@myhelix/cdk-watchful.IWatchful)

---

## Classes <a name="Classes"></a>

### WatchfulAspect <a name="@myhelix/cdk-watchful.WatchfulAspect"></a>
Expand Down
2 changes: 2 additions & 0 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/aspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as ecs_patterns from '@aws-cdk/aws-ecs-patterns';
import * as firehose from '@aws-cdk/aws-kinesisfirehose';
import * as lambda from '@aws-cdk/aws-lambda';
import * as rds from '@aws-cdk/aws-rds';
import * as stepfunctions from '@aws-cdk/aws-stepfunctions';
import { IAspect, IConstruct } from '@aws-cdk/core';

export interface WatchfulAspectProps {
Expand Down Expand Up @@ -31,6 +32,12 @@ export interface WatchfulAspectProps {
*/
readonly lambdaFn?: boolean;

/**
* Automatically watch AWS state machines in the scope.
* @default true
*/
readonly stateMachine?: boolean;

/**
* Automatically watch RDS Aurora clusters in the scope.
* @default true
Expand Down Expand Up @@ -66,6 +73,7 @@ export class WatchfulAspect implements IAspect {
const watchDynamo = this.props.dynamodb === undefined ? true : this.props.dynamodb;
const watchLambda = this.props.lambdaFn === undefined ? true : this.props.lambdaFn;
const watchFirehose = this.props.firehose === undefined ? true : this.props.firehose;
const watchStateMachine = this.props.stateMachine === undefined ? true : this.props.stateMachine;
const watchRdsAuroraCluster = this.props.rdsaurora === undefined ? true : this.props.rdsaurora;
const watchFargateEcs = this.props.fargateecs === undefined ? true : this.props.fargateecs;
const watchEc2Ecs = this.props.ec2ecs === undefined ? true : this.props.ec2ecs;
Expand All @@ -86,6 +94,10 @@ export class WatchfulAspect implements IAspect {
this.watchful.watchLambdaFunction(node.node.path, node);
}

if (watchStateMachine && node instanceof stepfunctions.StateMachine) {
this.watchful.watchStateMachine(node.node.path, node);
}

if (watchRdsAuroraCluster && node instanceof rds.DatabaseCluster) {
this.watchful.watchRdsAuroraCluster(node.node.path, node);
}
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from './dynamodb';
export * from './ecs';
export * from './firehose';
export * from './lambda';
export * from './state-machine';
export * from './rds-aurora';
export * from './sqs';
export * from './sqs';
37 changes: 37 additions & 0 deletions src/monitoring/aws/state-machine/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Metric, Statistic } from '@aws-cdk/aws-cloudwatch';
import { Duration } from '@aws-cdk/core';

const enum Metrics {
ExecutionsStarted = 'ExecutionsStarted',
ExecutionsSucceeded = 'ExecutionsSucceeded',
ExecutionsFailed = 'ExecutionsFailed',
ExecutionsAborted = 'ExecutionsAborted',
ExecutionThrottled ='ExecutionThrottled',
ExecutionsTimedOut = 'ExecutionsTimedOut'
}

const Namespace = 'AWS/States';

export class StateMachineMetricFactory {
metricExecutions(stateMachineArn: string) {
return {
total: this.metric(Metrics.ExecutionsStarted, stateMachineArn).with({ label: 'Total', statistic: Statistic.SUM }),
succeeded: this.metric(Metrics.ExecutionsSucceeded, stateMachineArn).with({ label: 'Failed Succeeded', statistic: Statistic.SUM }),
failed: this.metric(Metrics.ExecutionsFailed, stateMachineArn).with({ label: 'Failed Executions', statistic: Statistic.SUM }),
aborted: this.metric(Metrics.ExecutionsAborted, stateMachineArn).with({ label: 'Aborted Executions', statistic: Statistic.SUM }),
throttled: this.metric(Metrics.ExecutionThrottled, stateMachineArn).with({ label: 'Executions Throttled', statistic: Statistic.SUM }),
timedOut: this.metric(Metrics.ExecutionsTimedOut, stateMachineArn).with({ label: 'Executions TimedOut', statistic: Statistic.SUM }),
};
}

protected metric(metric: Metrics, stateMachineArn: string) {
return new Metric({
metricName: metric,
namespace: Namespace,
period: Duration.minutes(1),
dimensions: {
StateMachineArn: stateMachineArn,
},
});
}
}
74 changes: 74 additions & 0 deletions src/state-machine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ComparisonOperator, GraphWidget } from '@aws-cdk/aws-cloudwatch';
import { StateMachine } from '@aws-cdk/aws-stepfunctions';
import { Construct, Duration } from '@aws-cdk/core';
import { IWatchful } from './api';
import { StateMachineMetricFactory } from './monitoring/aws/state-machine/metrics';

export interface WatchStateMachineOptions {
/**
* Alarm when execution failures reach this threshold over 1 minute.
*
* @default 1 any execution failure will trigger the alarm
*/
readonly metricFailedThreshold?: number;
}

export interface WatchStateMachineProps extends WatchStateMachineOptions {
readonly title: string;
readonly watchful: IWatchful;
readonly stateMachine: StateMachine;
}

export class WatchStateMachine extends Construct {
private readonly title: string;
private readonly watchful: IWatchful;
private readonly stateMachine: StateMachine;
private readonly metricFailedThreshold: number;

private readonly metrics: StateMachineMetricFactory;

constructor(scope: Construct, id: string, props: WatchStateMachineProps) {
super(scope, id);

this.title = props.title;
this.watchful = props.watchful;
this.stateMachine = props.stateMachine;

this.metricFailedThreshold = props.metricFailedThreshold ?? 1;

this.metrics = new StateMachineMetricFactory();

this.createLinks();
this.createExecutionMetrics();
}

private createExecutionMetrics() {
const execMetrics = this.metrics.metricExecutions(this.stateMachine.stateMachineArn);
const { failed } = execMetrics;
failed.createAlarm(this, 'ExecutionFailures', {
alarmDescription: `at ${this.metricFailedThreshold}`,
threshold: this.metricFailedThreshold,
period: Duration.minutes(5),
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
evaluationPeriods: 1,
statistic: 'sum',
});

this.watchful.addWidgets(new GraphWidget({
title: 'Overall Execution/min',
width: 12,
stacked: false,
left: Object.values(execMetrics),
leftAnnotations: [{ value: this.metricFailedThreshold, color: '#ff0000', label: 'Execution Failure Alarm' }],
}));
}

private createLinks() {
this.watchful.addSection(this.title, {
links: [{
title: 'Amazon State Machine',
url: `https://console.aws.amazon.com/states/home?region=${this.stateMachine.stack.region}#/statemachines/view/${this.stateMachine.stateMachineArn}`,
}],
});
}
}
8 changes: 8 additions & 0 deletions src/watchful.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as rds from '@aws-cdk/aws-rds';
import * as sns from '@aws-cdk/aws-sns';
import * as sns_subscriptions from '@aws-cdk/aws-sns-subscriptions';
import * as sqs from '@aws-cdk/aws-sqs';
import * as stepfunctions from '@aws-cdk/aws-stepfunctions';
import { Construct, CfnOutput, Aspects } from '@aws-cdk/core';
import { IWatchful, SectionOptions } from './api';
import { WatchApiGatewayOptions, WatchApiGateway } from './api-gateway';
Expand All @@ -20,6 +21,7 @@ import { WatchFirehoseServiceOptions, WatchFirehoseService } from './firehose';
import { WatchLambdaFunctionOptions, WatchLambdaFunction } from './lambda';
import { WatchRdsAuroraOptions, WatchRdsAurora } from './rds-aurora';
import { WatchSqsOptions, WatchSqsService } from './sqs';
import { WatchStateMachineOptions, WatchStateMachine } from './state-machine';

export interface WatchfulProps {
readonly alarmEmail?: string;
Expand Down Expand Up @@ -121,6 +123,12 @@ export class Watchful extends Construct implements IWatchful {
});
}

public watchStateMachine(title: string, stateMachine: stepfunctions.StateMachine, options: WatchStateMachineOptions = {}) {
return new WatchStateMachine(this, stateMachine.node.uniqueId, {
title, watchful: this, stateMachine, ...options,
});
}

public watchFargateEcs( title: string, fargateService: ecs.FargateService, targetGroup: ApplicationTargetGroup,
options: WatchEcsServiceOptions = {}) {
return new WatchEcsService(this, fargateService.node.addr, {
Expand Down
Loading

0 comments on commit 238e2af

Please sign in to comment.