From ef8c56835696f6ddac75aac5576c26be1f76fb15 Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Fri, 8 Mar 2019 18:56:24 +0100 Subject: [PATCH] feat(ec2): add vpn metrics Add VPN metrics (TunnelState, TunnelDataIn, TunnelDataOut) across all tunnels and per connection by adding a new augmentation. Adapt AugmentationGenerator to support name overrides for class, interface and module when these cannot be directly derived from the CloudFormation resource name: no base class, resource name not really Pascal case, resource module is Kebab case. --- packages/@aws-cdk/aws-ec2/lib/index.ts | 2 + packages/@aws-cdk/aws-ec2/lib/vpn.ts | 39 ++++++++++++++++++ packages/@aws-cdk/aws-ec2/package.json | 4 +- packages/@aws-cdk/aws-ec2/test/test.vpn.ts | 41 ++++++++++++++++++- .../augmentations/AWS_EC2_VPNConnection.json | 27 ++++++++++++ .../cfnspec/lib/schema/augmentation.ts | 24 ++++++++++- tools/cfn2ts/lib/augmentation-generator.ts | 10 ++--- 7 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 packages/@aws-cdk/cfnspec/lib/augmentations/AWS_EC2_VPNConnection.json diff --git a/packages/@aws-cdk/aws-ec2/lib/index.ts b/packages/@aws-cdk/aws-ec2/lib/index.ts index 1188dff779727..fdfe81aa9b97a 100644 --- a/packages/@aws-cdk/aws-ec2/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2/lib/index.ts @@ -10,3 +10,5 @@ export * from './vpn'; // AWS::EC2 CloudFormation Resources: export * from './ec2.generated'; + +import './ec2-augmentations.generated'; diff --git a/packages/@aws-cdk/aws-ec2/lib/vpn.ts b/packages/@aws-cdk/aws-ec2/lib/vpn.ts index bab383c4b8aee..d0860d78f9e03 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpn.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpn.ts @@ -1,3 +1,4 @@ +import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import cdk = require('@aws-cdk/cdk'); import net = require('net'); import { CfnCustomerGateway, CfnVPNConnection, CfnVPNConnectionRoute } from './ec2.generated'; @@ -98,6 +99,44 @@ export enum VpnConnectionType { } export class VpnConnection extends cdk.Construct implements IVpnConnection { + /** + * Return the given named metric for all VPN connections. + */ + public static metricAll(metricName: string, props?: cloudwatch.MetricCustomization): cloudwatch.Metric { + return new cloudwatch.Metric({ + namespace: 'AWS/VPN', + metricName, + ...props + }); + } + + /** + * Metric for the tunnel state of all VPN connections. + * + * @default average over 5 minutes + */ + public static metricAllTunnelState(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { + return this.metricAll('TunnelSate', { statistic: 'avg', ...props }); + } + + /** + * Metric for the tunnel data in of all VPN connections. + * + * @default sum over 5 minutes + */ + public static metricAllTunnelDataIn(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { + return this.metricAll('TunnelDataIn', { statistic: 'sum', ...props }); + } + + /** + * Metric for the tunnel data out of all VPN connections. + * + * @default sum over 5 minutes + */ + public static metricAllTunnelDataOut(props?: cloudwatch.MetricCustomization): cloudwatch.Metric { + return this.metricAll('TunnelDataOut', { statistic: 'sum', ...props }); + } + public readonly vpnId: string; public readonly customerGatewayId: string; public readonly customerGatewayIp: string; diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index bedd96bbedd73..e23ca395a28b2 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -62,11 +62,13 @@ }, "dependencies": { "@aws-cdk/aws-iam": "^0.25.2", + "@aws-cdk/aws-cloudwatch": "^0.25.2", "@aws-cdk/cdk": "^0.25.2", "@aws-cdk/cx-api": "^0.25.2" }, "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { + "@aws-cdk/aws-cloudwatch": "^0.25.2", "@aws-cdk/cdk": "^0.25.2", "@aws-cdk/cx-api": "^0.25.2" }, @@ -78,4 +80,4 @@ "resource-attribute:@aws-cdk/aws-ec2.ISecurityGroup.securityGroupVpcId" ] } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-ec2/test/test.vpn.ts b/packages/@aws-cdk/aws-ec2/test/test.vpn.ts index 6dcfd3e92e29b..3d88da1409339 100644 --- a/packages/@aws-cdk/aws-ec2/test/test.vpn.ts +++ b/packages/@aws-cdk/aws-ec2/test/test.vpn.ts @@ -1,7 +1,7 @@ import { expect, haveResource, } from '@aws-cdk/assert'; import { Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { VpcNetwork } from '../lib'; +import { VpcNetwork, VpnConnection } from '../lib'; export = { 'can add a vpn connection to a vpc with a vpn gateway'(test: Test) { @@ -258,6 +258,45 @@ export = { } }), /`tunnelInsideCidr`.+size/); + test.done(); + }, + + 'can use metricTunnelState on a vpn connection'(test: Test) { + // GIVEN + const stack = new Stack(); + + const vpc = new VpcNetwork(stack, 'VpcNetwork', { + vpnGateway: true + }); + + const vpn = vpc.addVpnConnection('Vpn', { + ip: '192.0.2.1' + }); + + // THEN + test.deepEqual(stack.node.resolve(vpn.metricTunnelState()), { + dimensions: { VpnId: { Ref: 'VpcNetworkVpnA476C58D' } }, + namespace: 'AWS/VPN', + metricName: 'TunnelState', + periodSec: 300, + statistic: 'Average' + }); + + test.done(); + }, + + 'can use metricAllTunnelDataOut'(test: Test) { + // GIVEN + const stack = new Stack(); + + // THEN + test.deepEqual(stack.node.resolve(VpnConnection.metricAllTunnelDataOut()), { + namespace: 'AWS/VPN', + metricName: 'TunnelDataOut', + periodSec: 300, + statistic: 'Sum' + }); + test.done(); } }; diff --git a/packages/@aws-cdk/cfnspec/lib/augmentations/AWS_EC2_VPNConnection.json b/packages/@aws-cdk/cfnspec/lib/augmentations/AWS_EC2_VPNConnection.json new file mode 100644 index 0000000000000..1f977c2fbfdda --- /dev/null +++ b/packages/@aws-cdk/cfnspec/lib/augmentations/AWS_EC2_VPNConnection.json @@ -0,0 +1,27 @@ +{ + "overrides": { + "interface": "IVpnConnection", + "class": "VpnConnection", + "module": "vpn" + }, + "metrics": { + "namespace": "AWS/VPN", + "dimensions": { "VpnId": "this.vpnId" }, + "metrics": [ + { + "name": "TunnelState", + "documentation": "The state of the tunnel. 0 indicates DOWN and 1 indicates UP." + }, + { + "name": "TunnelDataIn", + "documentation": "The bytes received through the VPN tunnel.", + "type": "count" + }, + { + "name": "TunnelDataOut", + "documentation": "The bytes sent through the VPN tunnel.", + "type": "count" + } + ] + } +} diff --git a/packages/@aws-cdk/cfnspec/lib/schema/augmentation.ts b/packages/@aws-cdk/cfnspec/lib/schema/augmentation.ts index a47a16a96e0fd..d7deed5eb68f9 100644 --- a/packages/@aws-cdk/cfnspec/lib/schema/augmentation.ts +++ b/packages/@aws-cdk/cfnspec/lib/schema/augmentation.ts @@ -6,6 +6,28 @@ export interface ResourceAugmentation { * Metric augmentations for this resource type */ metrics?: ResourceMetricAugmentations; + + /** + * Overrides for this resource augmentation + */ + overrides?: ResourceOverrides; +} + +export interface ResourceOverrides { + /** + * The name of the resource class + */ + class?: string; + + /** + * The name of the resource interface + */ + interface?: string; + + /** + * The name of the module + */ + module?: string; } export interface ResourceMetricAugmentations { @@ -57,4 +79,4 @@ export enum MetricType { * property. The most useful aggregate of this type of metric is "Max". */ Gauge = 'gauge' -} \ No newline at end of file +} diff --git a/tools/cfn2ts/lib/augmentation-generator.ts b/tools/cfn2ts/lib/augmentation-generator.ts index 34cc57c82c825..3848d4eece6de 100644 --- a/tools/cfn2ts/lib/augmentation-generator.ts +++ b/tools/cfn2ts/lib/augmentation-generator.ts @@ -24,7 +24,7 @@ export class AugmentationGenerator { if (aug.metrics) { this.code.line('import cloudwatch = require("@aws-cdk/aws-cloudwatch");'); - this.emitMetricAugmentations(resourceTypeName, aug.metrics); + this.emitMetricAugmentations(resourceTypeName, aug.metrics, aug.overrides); hadAugmentations = true; } } @@ -39,14 +39,14 @@ export class AugmentationGenerator { return await this.code.save(dir); } - private emitMetricAugmentations(resourceTypeName: string, metrics: schema.ResourceMetricAugmentations) { + private emitMetricAugmentations(resourceTypeName: string, metrics: schema.ResourceMetricAugmentations, overrides?: schema.ResourceOverrides) { const cfnName = SpecName.parse(resourceTypeName); const resourceName = genspec.CodeName.forCfnResource(cfnName, this.affix); const l2ClassName = resourceName.className.replace(/^Cfn/, ''); - const baseClassName = l2ClassName + 'Base'; - const interfaceName = 'I' + l2ClassName; - const baseClassModule = `./${l2ClassName.toLowerCase()}-base`; + const baseClassName = (overrides && overrides.class) || l2ClassName + 'Base'; + const interfaceName = (overrides && overrides.interface) || 'I' + l2ClassName; + const baseClassModule = `./${(overrides && overrides.module) || `${l2ClassName.toLowerCase()}-base`}`; this.code.line(`import { ${baseClassName} } from "${baseClassModule}";`);