Skip to content

Commit

Permalink
feat(appconfig): support for CfnMonitorsProperty in environments (#27680
Browse files Browse the repository at this point in the history
)

Adding support to associate our L1 monitors constructs (CfnMonitorsProperty) to a L2 environment construct.

**BREAKING CHANGE**
To define monitors under an environment, you now need to call a static method on `Monitor`.
Example:

```
new Environment(this, 'MyEnv', {
   ....
   monitors: [
      Monitor.fromCloudWatchAlarm(...),
      Monitor.fromCfnMonitorProperty(...),
   ],
});
```

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
chenjane-dev committed Oct 26, 2023
1 parent a4142aa commit 05f3453
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 63 deletions.
6 changes: 4 additions & 2 deletions packages/@aws-cdk/aws-appconfig-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,13 @@ declare const alarm: cloudwatch.Alarm;
new appconfig.Environment(this, 'MyEnvironment', {
application,
monitors: [
{alarm},
]
appconfig.Monitor.fromCloudWatchAlarm(alarm),
],
});
```

Environment monitors also support L1 CfnEnvironment.MonitorsProperty constructs. However, this is not the recommended approach for CloudWatch alarms because a role will not be auto-generated if not provided.

## Extension

An extension augments your ability to inject logic or behavior at different points during the AWS AppConfig workflow of creating or deploying a configuration.
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-appconfig-alpha/lib/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,15 @@ export interface HostedConfigurationOptions extends ConfigurationOptions {

/**
* The latest version number of the hosted configuration.
*
* @default - None.
*/
readonly latestVersionNumber?: number;

/**
* The version label of the hosted configuration.
*
* @default - None.
*/
readonly versionLabel?: string;
}
Expand All @@ -352,11 +356,15 @@ export interface HostedConfigurationProps extends ConfigurationProps {

/**
* The latest version number of the hosted configuration.
*
* @default - None.
*/
readonly latestVersionNumber?: number;

/**
* The version label of the hosted configuration.
*
* @default - None.
*/
readonly versionLabel?: string;
}
Expand Down
62 changes: 53 additions & 9 deletions packages/@aws-cdk/aws-appconfig-alpha/lib/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ export interface EnvironmentAttributes {

/**
* The name of the environment.
*
* @default - None.
*/
readonly name?: string;

/**
* The description of the environment.
*
* @default - None.
*/
readonly description?: string;

/**
* The monitors for the environment.
*
* @default - None.
*/
readonly monitors?: Monitor[];
}
Expand Down Expand Up @@ -238,8 +244,10 @@ export class Environment extends EnvironmentBase {
description: this.description,
monitors: this.monitors?.map((monitor, index) => {
return {
alarmArn: monitor.alarm.alarmArn,
alarmRoleArn: monitor.alarmRole?.roleArn || this.createAlarmRole(monitor.alarm.alarmArn, index).roleArn,
alarmArn: monitor.alarmArn,
...(monitor.monitorType === MonitorType.CLOUDWATCH
? { alarmRoleArn: monitor.alarmRoleArn || this.createAlarmRole(monitor.alarmArn, index).roleArn }
: { alarmRoleArn: monitor.alarmRoleArn }),
};
}),
});
Expand Down Expand Up @@ -276,21 +284,57 @@ export class Environment extends EnvironmentBase {
}
}

export enum MonitorType {
CLOUDWATCH,
CFN_MONITORS_PROPERTY,
}

/**
* Defines monitors that will be associated with an AWS AppConfig environment.
*/
export interface Monitor {
export abstract class Monitor {
/**
* The Amazon CloudWatch alarm.
*/
readonly alarm: cloudwatch.IAlarm;
* Creates a Monitor from a CloudWatch alarm. If the alarm role is not specified, a role will
* be generated.
*
* @param alarm The Amazon CloudWatch alarm.
* @param alarmRole The IAM role for AWS AppConfig to view the alarm state.
*/
public static fromCloudWatchAlarm(alarm: cloudwatch.IAlarm, alarmRole?: iam.IRole): Monitor {
return {
alarmArn: alarm.alarmArn,
alarmRoleArn: alarmRole?.roleArn,
monitorType: MonitorType.CLOUDWATCH,
};
}

/**
* The IAM role for AWS AppConfig to view the alarm state.
* Creates a Monitor from a CfnEnvironment.MonitorsProperty construct.
*
* @default - A role is generated.
* @param monitorsProperty The monitors property.
*/
public static fromCfnMonitorsProperty(monitorsProperty: CfnEnvironment.MonitorsProperty): Monitor {
return {
alarmArn: monitorsProperty.alarmArn!,
alarmRoleArn: monitorsProperty.alarmRoleArn,
monitorType: MonitorType.CFN_MONITORS_PROPERTY,
};
}

/**
* The alarm ARN for AWS AppConfig to monitor.
*/
public abstract readonly alarmArn: string;

/**
* The type of monitor.
*/
public abstract readonly monitorType: MonitorType;

/**
* The IAM role ARN for AWS AppConfig to view the alarm state.
*/
readonly alarmRole?: iam.IRole;
public abstract readonly alarmRoleArn?: string;
}

export interface IEnvironment extends IResource {
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-appconfig-alpha/lib/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,21 +290,29 @@ export interface ExtensionAttributes {

/**
* The Amazon Resource Name (ARN) of the extension.
*
* @default - The extension ARN is generated.
*/
readonly extensionArn?: string;

/**
* The actions of the extension.
*
* @default - None.
*/
readonly actions?: Action[];

/**
* The name of the extension.
*
* @default - None.
*/
readonly name?: string;

/**
* The description of the extension.
*
* @default - None.
*/
readonly description?: string;
}
Expand Down
141 changes: 96 additions & 45 deletions packages/@aws-cdk/aws-appconfig-alpha/test/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { App } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { Alarm, Metric } from 'aws-cdk-lib/aws-cloudwatch';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Application, Environment } from '../lib';
import { Application, Environment, Monitor } from '../lib';

describe('environment', () => {
test('default environment', () => {
Expand Down Expand Up @@ -58,26 +58,23 @@ describe('environment', () => {
test('environment with monitors with alarm and alarmRole', () => {
const stack = new cdk.Stack();
const app = new Application(stack, 'MyAppConfig');
const alarm = new Alarm(stack, 'Alarm', {
threshold: 5,
evaluationPeriods: 5,
metric: new Metric(
{
namespace: 'aws',
metricName: 'myMetric',
},
),
});
const alarmRole = new iam.Role(stack, 'Role', {
assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'),
});
const env = new Environment(stack, 'MyEnvironment', {
name: 'TestEnv',
application: app,
monitors: [
{
alarm: new Alarm(stack, 'Alarm', {
threshold: 5,
evaluationPeriods: 5,
metric: new Metric(
{
namespace: 'aws',
metricName: 'myMetric',
},
),
}),
alarmRole: new iam.Role(stack, 'Role', {
assumedBy: new iam.ServicePrincipal('appconfig.amazonaws.com'),
}),
},
],
monitors: [Monitor.fromCloudWatchAlarm(alarm, alarmRole)],
});

Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 1);
Expand Down Expand Up @@ -123,11 +120,7 @@ describe('environment', () => {
const env = new Environment(stack, 'MyEnvironment', {
name: 'TestEnv',
application: app,
monitors: [
{
alarm,
},
],
monitors: [Monitor.fromCloudWatchAlarm(alarm)],
});

expect(env).toBeDefined();
Expand Down Expand Up @@ -177,39 +170,97 @@ describe('environment', () => {
});
});

test('environment with monitors with two alarms', () => {
test('environment with CfnMonitorsProperty monitor', () => {
const stack = new cdk.Stack();
const app = new Application(stack, 'MyAppConfig');
new Environment(stack, 'MyEnvironment', {
const env = new Environment(stack, 'MyEnvironment', {
name: 'TestEnv',
application: app,
monitors: [
Monitor.fromCfnMonitorsProperty({
alarmArn: 'thisismyalarm',
}),
],
});

expect(env).toBeDefined();
Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 0);
Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 0);
Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', {
Name: 'TestEnv',
ApplicationId: {
Ref: 'MyAppConfigB4B63E75',
},
Monitors: [
{
alarm: new Alarm(stack, 'Alarm1', {
threshold: 5,
evaluationPeriods: 5,
metric: new Metric(
{
namespace: 'aws',
metricName: 'myMetric',
},
),
}),
AlarmArn: 'thisismyalarm',
},
],
});
});

test('environment with CfnMonitorsProperty monitor with roleArn', () => {
const stack = new cdk.Stack();
const app = new Application(stack, 'MyAppConfig');
const env = new Environment(stack, 'MyEnvironment', {
name: 'TestEnv',
application: app,
monitors: [
Monitor.fromCfnMonitorsProperty({
alarmArn: 'thisismyalarm',
alarmRoleArn: 'thisismyalarmrolearn',
}),
],
});

expect(env).toBeDefined();
Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 0);
Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 0);
Template.fromStack(stack).hasResourceProperties('AWS::AppConfig::Environment', {
Name: 'TestEnv',
ApplicationId: {
Ref: 'MyAppConfigB4B63E75',
},
Monitors: [
{
alarm: new Alarm(stack, 'Alarm2', {
threshold: 5,
evaluationPeriods: 5,
metric: new Metric(
{
namespace: 'aws',
metricName: 'myMetric',
},
),
}),
AlarmArn: 'thisismyalarm',
AlarmRoleArn: 'thisismyalarmrolearn',
},
],
});
});

test('environment with monitors with two alarms', () => {
const stack = new cdk.Stack();
const app = new Application(stack, 'MyAppConfig');
const alarm1 = new Alarm(stack, 'Alarm1', {
threshold: 5,
evaluationPeriods: 5,
metric: new Metric(
{
namespace: 'aws',
metricName: 'myMetric',
},
),
});
const alarm2 = new Alarm(stack, 'Alarm2', {
threshold: 5,
evaluationPeriods: 5,
metric: new Metric(
{
namespace: 'aws',
metricName: 'myMetric',
},
),
});
new Environment(stack, 'MyEnvironment', {
name: 'TestEnv',
application: app,
monitors: [
Monitor.fromCloudWatchAlarm(alarm1),
Monitor.fromCloudWatchAlarm(alarm2),
],
});

Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 2);
Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 2);
Expand Down

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

Loading

0 comments on commit 05f3453

Please sign in to comment.