@guardian/cdk 48.2.1 → 48.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/aspects/cfn-parameter-reporter.d.ts +9 -0
- package/lib/aspects/cfn-parameter-reporter.d.ts.map +1 -0
- package/lib/aspects/cfn-parameter-reporter.js +21 -0
- package/lib/aspects/cfn-parameter-reporter.js.map +1 -0
- package/lib/constructs/core/stack.d.ts.map +1 -1
- package/lib/constructs/core/stack.js +2 -0
- package/lib/constructs/core/stack.js.map +1 -1
- package/lib/experimental/constructs/error-budget-alarm.d.ts +18 -0
- package/lib/experimental/constructs/error-budget-alarm.d.ts.map +1 -1
- package/lib/experimental/constructs/error-budget-alarm.js +8 -0
- package/lib/experimental/constructs/error-budget-alarm.js.map +1 -1
- package/lib/experimental/constructs/index.d.ts +3 -0
- package/lib/experimental/constructs/index.d.ts.map +1 -0
- package/lib/experimental/constructs/index.js +19 -0
- package/lib/experimental/constructs/index.js.map +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IAspect } from "aws-cdk-lib";
|
|
2
|
+
import type { IConstruct } from "constructs";
|
|
3
|
+
/**
|
|
4
|
+
* An Aspect to remind users to check Parameters to ensure Riff-Raff can successfully deploy the stack.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CfnParameterReporter implements IAspect {
|
|
7
|
+
visit(node: IConstruct): void;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=cfn-parameter-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cfn-parameter-reporter.d.ts","sourceRoot":"","sources":["../../src/aspects/cfn-parameter-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,qBAAa,oBAAqB,YAAW,OAAO;IAC3C,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;CAarC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CfnParameterReporter = void 0;
|
|
4
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
5
|
+
/**
|
|
6
|
+
* An Aspect to remind users to check Parameters to ensure Riff-Raff can successfully deploy the stack.
|
|
7
|
+
*/
|
|
8
|
+
class CfnParameterReporter {
|
|
9
|
+
visit(node) {
|
|
10
|
+
if (node instanceof aws_cdk_lib_1.CfnParameter) {
|
|
11
|
+
const defaultValue = node.default;
|
|
12
|
+
const hasDefault = !!defaultValue;
|
|
13
|
+
const isSsmParameter = node.type.startsWith("AWS::SSM::Parameter::Value");
|
|
14
|
+
if (isSsmParameter && hasDefault) {
|
|
15
|
+
aws_cdk_lib_1.Annotations.of(node.stack).addInfo(`Stack reads the SSM Parameter '${defaultValue}'. Ensure it exists prior to deployment.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CfnParameterReporter = CfnParameterReporter;
|
|
21
|
+
//# sourceMappingURL=cfn-parameter-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cfn-parameter-reporter.js","sourceRoot":"","sources":["../../src/aspects/cfn-parameter-reporter.ts"],"names":[],"mappings":";;;AACA,6CAAwD;AAGxD;;GAEG;AACH,MAAa,oBAAoB;IACxB,KAAK,CAAC,IAAgB;QAC3B,IAAI,IAAI,YAAY,0BAAY,EAAE;YAChC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAiB,CAAC;YAC5C,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC;YAClC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,4BAA4B,CAAC,CAAC;YAE1E,IAAI,cAAc,IAAI,UAAU,EAAE;gBAChC,yBAAW,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAChC,kCAAkC,YAAY,0CAA0C,CACzF,CAAC;aACH;SACF;IACH,CAAC;CACF;AAdD,oDAcC","sourcesContent":["import type { IAspect } from \"aws-cdk-lib\";\nimport { Annotations, CfnParameter } from \"aws-cdk-lib\";\nimport type { IConstruct } from \"constructs\";\n\n/**\n * An Aspect to remind users to check Parameters to ensure Riff-Raff can successfully deploy the stack.\n */\nexport class CfnParameterReporter implements IAspect {\n public visit(node: IConstruct): void {\n if (node instanceof CfnParameter) {\n const defaultValue = node.default as string;\n const hasDefault = !!defaultValue;\n const isSsmParameter = node.type.startsWith(\"AWS::SSM::Parameter::Value\");\n\n if (isSsmParameter && hasDefault) {\n Annotations.of(node.stack).addInfo(\n `Stack reads the SSM Parameter '${defaultValue}'. Ensure it exists prior to deployment.`\n );\n }\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../src/constructs/core/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,GAAG,EAAW,YAAY,EAA0B,KAAK,EAAQ,MAAM,aAAa,CAAC;AAC3G,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../src/constructs/core/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,GAAG,EAAW,YAAY,EAA0B,KAAK,EAAQ,MAAM,aAAa,CAAC;AAC3G,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAO7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC;IACjE;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,OAAQ,SAAQ,KAAM,YAAW,kBAAkB;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED;;;;;;;;;OASG;IACH,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,wBAAwB,GAAE,OAAc,GAAG,IAAI;IAM5F;;;;OAIG;IACH,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAK7C;gBAGW,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY;IAgCrD;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqDG;IACI,iBAAiB,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,iBAAiB,GAAG,IAAI;CAQhG;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,OAAO;gBAEzC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY;IAI3C,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF"}
|
|
@@ -8,6 +8,7 @@ const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
|
8
8
|
const assertions_1 = require("aws-cdk-lib/assertions");
|
|
9
9
|
const git_url_parse_1 = __importDefault(require("git-url-parse"));
|
|
10
10
|
const cfn_include_reporter_1 = require("../../aspects/cfn-include-reporter");
|
|
11
|
+
const cfn_parameter_reporter_1 = require("../../aspects/cfn-parameter-reporter");
|
|
11
12
|
const metadata_1 = require("../../aspects/metadata");
|
|
12
13
|
const constants_1 = require("../../constants");
|
|
13
14
|
const git_1 = require("../../utils/git");
|
|
@@ -61,6 +62,7 @@ class GuStack extends aws_cdk_lib_1.Stack {
|
|
|
61
62
|
aws_cdk_lib_1.Aspects.of(this).add(new metadata_1.Metadata(this));
|
|
62
63
|
}
|
|
63
64
|
aws_cdk_lib_1.Aspects.of(this).add(new cfn_include_reporter_1.CfnIncludeReporter());
|
|
65
|
+
aws_cdk_lib_1.Aspects.of(this).add(new cfn_parameter_reporter_1.CfnParameterReporter());
|
|
64
66
|
}
|
|
65
67
|
get stage() {
|
|
66
68
|
return this._stage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../../../src/constructs/core/stack.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotations, App, Aspects, CfnParameter, LegacyStackSynthesizer, Stack, Tags } from \"aws-cdk-lib\";\nimport type { CfnElement, StackProps } from \"aws-cdk-lib\";\nimport { Template } from \"aws-cdk-lib/assertions\";\nimport type { IConstruct } from \"constructs\";\nimport gitUrlParse from \"git-url-parse\";\nimport { CfnIncludeReporter } from \"../../aspects/cfn-include-reporter\";\nimport { Metadata } from \"../../aspects/metadata\";\nimport { ContextKeys, MetadataKeys, TrackingTag } from \"../../constants\";\nimport { gitRemoteOriginUrl } from \"../../utils/git\";\nimport type { StackStageIdentity } from \"./identity\";\nimport type { GuStaticLogicalId } from \"./migrating\";\n\nexport interface GuStackProps extends Omit<StackProps, \"stackName\"> {\n /**\n * The Guardian stack being used (as defined in your riff-raff.yaml).\n * This will be applied as a tag to all of your resources.\n */\n stack: string;\n\n stage: string;\n\n /**\n * The AWS CloudFormation stack name (as shown in the AWS CloudFormation UI).\n * @defaultValue the `GU_CFN_STACK_NAME` environment variable\n */\n cloudFormationStackName?: string;\n /**\n * Set this to true to stop the GuStack from tagging all of your AWS resources.\n * This should only be turned on as part of an initial migration from CloudFormation.\n */\n withoutTags?: boolean;\n\n /**\n * Set to disable CDK metadata. Only for internal use (for disabling for some\n * snapshot tests). We rely on tracking data to prioritise future work so\n * please do not override this.\n */\n withoutMetadata?: boolean;\n}\n\n/**\n * GuStack provides the `stack` and `stage` parameters to a template.\n * It also takes the `app` in the constructor.\n *\n * GuStack will add the Stack, Stage and App tags to all resources.\n *\n * GuStack also adds the tag `X-Gu-CDK-Version`.\n * This tag allows us to measure adoption of this library.\n * It's value is the version of guardian/cdk being used, as defined in `package.json`.\n * As a result, the change sets between version numbers will be fairly noisy,\n * as all resources receive a tag update.\n * It is recommended to upgrade the version of @guardian/cdk being used in two steps:\n * 1. Bump the library, apply the tag updates\n * 2. Make any other stack changes\n *\n * Typical usage is to extend GuStack:\n *\n * ```typescript\n * class MyStack extends GuStack {\n * constructor(scope: App, id: string, props: GuStackProps) {\n * super(scope, id, props)\n * }\n *\n * // add resources here\n * }\n * ```\n */\nexport class GuStack extends Stack implements StackStageIdentity {\n private readonly _stack: string;\n private readonly _stage: string;\n\n get stage(): string {\n return this._stage;\n }\n\n get stack(): string {\n return this._stack;\n }\n\n /**\n * A helper function to add a tag to all resources in a stack.\n *\n * Note: tags will be listed in alphabetical order during synthesis.\n *\n * @param key the tag name\n * @param value the value of the tag\n * @param applyToLaunchedInstances whether or not to apply the tag to instances launched in an ASG.\n * @protected\n */\n protected addTag(key: string, value: string, applyToLaunchedInstances: boolean = true): void {\n // TODO add validation for `key` and `value`\n // see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html\n Tags.of(this).add(key, value, { applyToLaunchedInstances });\n }\n\n /**\n * Returns all the parameters on this stack.\n *\n * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html\n */\n get parameters(): Record<string, CfnParameter> {\n return this.node\n .findAll()\n .filter((construct) => construct instanceof CfnParameter)\n .reduce((acc, param) => ({ ...acc, [param.node.id]: param as CfnParameter }), {});\n }\n\n // eslint-disable-next-line custom-rules/valid-constructors -- GuStack is the exception as it must take an App\n constructor(app: App, id: string, props: GuStackProps) {\n const { cloudFormationStackName = process.env.GU_CFN_STACK_NAME, stack, stage, withoutTags } = props;\n\n super(app, id, {\n ...props,\n stackName: cloudFormationStackName,\n\n // TODO Use `DefaultStackSynthesizer` or create own synthesizer?\n // see https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html#bootstrapping-custom-synth\n synthesizer: new LegacyStackSynthesizer(),\n });\n\n this._stack = stack;\n this._stage = stage.toUpperCase();\n\n if (!withoutTags) {\n this.addTag(TrackingTag.Key, TrackingTag.Value);\n\n this.addTag(\"Stack\", this.stack);\n this.addTag(\"Stage\", this.stage);\n\n this.tryAddRepositoryTag();\n }\n\n if (!props.withoutMetadata) {\n Aspects.of(this).add(new Metadata(this));\n }\n\n Aspects.of(this).add(new CfnIncludeReporter());\n }\n\n /**\n * Adds a tag to resources in the stack for the repository name.\n * The value is retrieved in the following order:\n * 1. From the context\n * 2. From git config\n *\n * @private\n */\n private tryAddRepositoryTag(): void {\n try {\n const urlFromContext = this.node.tryGetContext(ContextKeys.REPOSITORY_URL) as string | undefined;\n const repositoryUrl: string = urlFromContext ?? gitRemoteOriginUrl();\n const repositoryName = gitUrlParse(repositoryUrl).full_name;\n this.addTag(MetadataKeys.REPOSITORY_NAME, repositoryName);\n } catch {\n console.info(\n `Unable to find git repository name. Set the ${ContextKeys.REPOSITORY_URL} context value or configure a git remote`\n );\n }\n }\n\n /**\n * Override the auto-generated logical ID for a resource, in the generated CloudFormation template, with a static one.\n *\n * Of particular use when migrating a JSON/YAML CloudFormation template into GuCDK.\n * It's generally advised to retain the logical ID for stateful resources, such as databases or buckets.\n *\n * Let's say we have a YAML template:\n *\n * ```yaml\n * AWSTemplateFormatVersion: '2010-09-09'\n * Resources:\n * UsesTable:\n * Type: AWS::DynamoDB::Table\n * Properties:\n * TableName: !Sub 'users-${stage}'\n * ```\n *\n * When moving to GuCDK we'll have this:\n *\n * class MyStack extends GuStack {\n * constructor(app: App, id: string, props: GuStackProps) {\n * super(app, id, props);\n *\n * const { stage } = this;\n *\n * new Table(this, \"UsersTable\", {\n * name: `users-${stage}`\n * });\n * }\n * }\n *\n * During synthesis, CDK auto-generates logical IDs, so we'll have a stack with a DynamoDB table named 'UsersTable<SOME GUID>', NOT `UsersTable`.\n * That is, the `UsersTable` table will be deleted.\n *\n * In order to retain the original ID from the YAML template, we will do:\n *\n * class MyStack extends GuStack {\n * constructor(app: App, id: string, props: GuStackProps) {\n * super(app, id, props);\n *\n * const { stage } = this;\n *\n * const table = new Table(this, \"UsersTable\", {\n * name: `users-${stage}`\n * });\n *\n * this.overrideLogicalId(table, { logicalId: \"UsersTable\", reason: \"Retaining a stateful resource from the YAML template\" });\n * }\n * }\n *\n * @param construct The (stateful) resource to retain the logical ID of.\n * @param logicalId The logical ID of the resource (as defined in the JSON/YAML template.\n * @param reason A small explanation to keep the logical ID. Mainly used to help future developers.\n */\n public overrideLogicalId(construct: IConstruct, { logicalId, reason }: GuStaticLogicalId): void {\n const {\n node: { id, defaultChild },\n } = construct;\n\n (defaultChild as CfnElement).overrideLogicalId(logicalId);\n Annotations.of(construct).addInfo(`Setting logical ID for ${id} to ${logicalId}. Reason: ${reason}`);\n }\n}\n\n/**\n * A GuStack but designed for Stack Set instances.\n *\n * In a stack set application, `GuStackForStackSetInstance` is used to represent the infrastructure to provision in target AWS accounts.\n */\nexport class GuStackForStackSetInstance extends GuStack {\n // eslint-disable-next-line custom-rules/valid-constructors -- GuStackForStackSet should have a unique `App`\n constructor(id: string, props: GuStackProps) {\n super(new App(), id, props);\n }\n\n get cfnJson(): string {\n return JSON.stringify(Template.fromStack(this).toJSON(), null, 2);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../../../src/constructs/core/stack.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotations, App, Aspects, CfnParameter, LegacyStackSynthesizer, Stack, Tags } from \"aws-cdk-lib\";\nimport type { CfnElement, StackProps } from \"aws-cdk-lib\";\nimport { Template } from \"aws-cdk-lib/assertions\";\nimport type { IConstruct } from \"constructs\";\nimport gitUrlParse from \"git-url-parse\";\nimport { CfnIncludeReporter } from \"../../aspects/cfn-include-reporter\";\nimport { CfnParameterReporter } from \"../../aspects/cfn-parameter-reporter\";\nimport { Metadata } from \"../../aspects/metadata\";\nimport { ContextKeys, MetadataKeys, TrackingTag } from \"../../constants\";\nimport { gitRemoteOriginUrl } from \"../../utils/git\";\nimport type { StackStageIdentity } from \"./identity\";\nimport type { GuStaticLogicalId } from \"./migrating\";\n\nexport interface GuStackProps extends Omit<StackProps, \"stackName\"> {\n /**\n * The Guardian stack being used (as defined in your riff-raff.yaml).\n * This will be applied as a tag to all of your resources.\n */\n stack: string;\n\n stage: string;\n\n /**\n * The AWS CloudFormation stack name (as shown in the AWS CloudFormation UI).\n * @defaultValue the `GU_CFN_STACK_NAME` environment variable\n */\n cloudFormationStackName?: string;\n /**\n * Set this to true to stop the GuStack from tagging all of your AWS resources.\n * This should only be turned on as part of an initial migration from CloudFormation.\n */\n withoutTags?: boolean;\n\n /**\n * Set to disable CDK metadata. Only for internal use (for disabling for some\n * snapshot tests). We rely on tracking data to prioritise future work so\n * please do not override this.\n */\n withoutMetadata?: boolean;\n}\n\n/**\n * GuStack provides the `stack` and `stage` parameters to a template.\n * It also takes the `app` in the constructor.\n *\n * GuStack will add the Stack, Stage and App tags to all resources.\n *\n * GuStack also adds the tag `X-Gu-CDK-Version`.\n * This tag allows us to measure adoption of this library.\n * It's value is the version of guardian/cdk being used, as defined in `package.json`.\n * As a result, the change sets between version numbers will be fairly noisy,\n * as all resources receive a tag update.\n * It is recommended to upgrade the version of @guardian/cdk being used in two steps:\n * 1. Bump the library, apply the tag updates\n * 2. Make any other stack changes\n *\n * Typical usage is to extend GuStack:\n *\n * ```typescript\n * class MyStack extends GuStack {\n * constructor(scope: App, id: string, props: GuStackProps) {\n * super(scope, id, props)\n * }\n *\n * // add resources here\n * }\n * ```\n */\nexport class GuStack extends Stack implements StackStageIdentity {\n private readonly _stack: string;\n private readonly _stage: string;\n\n get stage(): string {\n return this._stage;\n }\n\n get stack(): string {\n return this._stack;\n }\n\n /**\n * A helper function to add a tag to all resources in a stack.\n *\n * Note: tags will be listed in alphabetical order during synthesis.\n *\n * @param key the tag name\n * @param value the value of the tag\n * @param applyToLaunchedInstances whether or not to apply the tag to instances launched in an ASG.\n * @protected\n */\n protected addTag(key: string, value: string, applyToLaunchedInstances: boolean = true): void {\n // TODO add validation for `key` and `value`\n // see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html\n Tags.of(this).add(key, value, { applyToLaunchedInstances });\n }\n\n /**\n * Returns all the parameters on this stack.\n *\n * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html\n */\n get parameters(): Record<string, CfnParameter> {\n return this.node\n .findAll()\n .filter((construct) => construct instanceof CfnParameter)\n .reduce((acc, param) => ({ ...acc, [param.node.id]: param as CfnParameter }), {});\n }\n\n // eslint-disable-next-line custom-rules/valid-constructors -- GuStack is the exception as it must take an App\n constructor(app: App, id: string, props: GuStackProps) {\n const { cloudFormationStackName = process.env.GU_CFN_STACK_NAME, stack, stage, withoutTags } = props;\n\n super(app, id, {\n ...props,\n stackName: cloudFormationStackName,\n\n // TODO Use `DefaultStackSynthesizer` or create own synthesizer?\n // see https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html#bootstrapping-custom-synth\n synthesizer: new LegacyStackSynthesizer(),\n });\n\n this._stack = stack;\n this._stage = stage.toUpperCase();\n\n if (!withoutTags) {\n this.addTag(TrackingTag.Key, TrackingTag.Value);\n\n this.addTag(\"Stack\", this.stack);\n this.addTag(\"Stage\", this.stage);\n\n this.tryAddRepositoryTag();\n }\n\n if (!props.withoutMetadata) {\n Aspects.of(this).add(new Metadata(this));\n }\n\n Aspects.of(this).add(new CfnIncludeReporter());\n Aspects.of(this).add(new CfnParameterReporter());\n }\n\n /**\n * Adds a tag to resources in the stack for the repository name.\n * The value is retrieved in the following order:\n * 1. From the context\n * 2. From git config\n *\n * @private\n */\n private tryAddRepositoryTag(): void {\n try {\n const urlFromContext = this.node.tryGetContext(ContextKeys.REPOSITORY_URL) as string | undefined;\n const repositoryUrl: string = urlFromContext ?? gitRemoteOriginUrl();\n const repositoryName = gitUrlParse(repositoryUrl).full_name;\n this.addTag(MetadataKeys.REPOSITORY_NAME, repositoryName);\n } catch {\n console.info(\n `Unable to find git repository name. Set the ${ContextKeys.REPOSITORY_URL} context value or configure a git remote`\n );\n }\n }\n\n /**\n * Override the auto-generated logical ID for a resource, in the generated CloudFormation template, with a static one.\n *\n * Of particular use when migrating a JSON/YAML CloudFormation template into GuCDK.\n * It's generally advised to retain the logical ID for stateful resources, such as databases or buckets.\n *\n * Let's say we have a YAML template:\n *\n * ```yaml\n * AWSTemplateFormatVersion: '2010-09-09'\n * Resources:\n * UsesTable:\n * Type: AWS::DynamoDB::Table\n * Properties:\n * TableName: !Sub 'users-${stage}'\n * ```\n *\n * When moving to GuCDK we'll have this:\n *\n * class MyStack extends GuStack {\n * constructor(app: App, id: string, props: GuStackProps) {\n * super(app, id, props);\n *\n * const { stage } = this;\n *\n * new Table(this, \"UsersTable\", {\n * name: `users-${stage}`\n * });\n * }\n * }\n *\n * During synthesis, CDK auto-generates logical IDs, so we'll have a stack with a DynamoDB table named 'UsersTable<SOME GUID>', NOT `UsersTable`.\n * That is, the `UsersTable` table will be deleted.\n *\n * In order to retain the original ID from the YAML template, we will do:\n *\n * class MyStack extends GuStack {\n * constructor(app: App, id: string, props: GuStackProps) {\n * super(app, id, props);\n *\n * const { stage } = this;\n *\n * const table = new Table(this, \"UsersTable\", {\n * name: `users-${stage}`\n * });\n *\n * this.overrideLogicalId(table, { logicalId: \"UsersTable\", reason: \"Retaining a stateful resource from the YAML template\" });\n * }\n * }\n *\n * @param construct The (stateful) resource to retain the logical ID of.\n * @param logicalId The logical ID of the resource (as defined in the JSON/YAML template.\n * @param reason A small explanation to keep the logical ID. Mainly used to help future developers.\n */\n public overrideLogicalId(construct: IConstruct, { logicalId, reason }: GuStaticLogicalId): void {\n const {\n node: { id, defaultChild },\n } = construct;\n\n (defaultChild as CfnElement).overrideLogicalId(logicalId);\n Annotations.of(construct).addInfo(`Setting logical ID for ${id} to ${logicalId}. Reason: ${reason}`);\n }\n}\n\n/**\n * A GuStack but designed for Stack Set instances.\n *\n * In a stack set application, `GuStackForStackSetInstance` is used to represent the infrastructure to provision in target AWS accounts.\n */\nexport class GuStackForStackSetInstance extends GuStack {\n // eslint-disable-next-line custom-rules/valid-constructors -- GuStackForStackSet should have a unique `App`\n constructor(id: string, props: GuStackProps) {\n super(new App(), id, props);\n }\n\n get cfnJson(): string {\n return JSON.stringify(Template.fromStack(this).toJSON(), null, 2);\n }\n}\n"]}
|
|
@@ -2,10 +2,25 @@ import type { IMetric } from "aws-cdk-lib/aws-cloudwatch";
|
|
|
2
2
|
import { Construct } from "constructs";
|
|
3
3
|
import type { GuStack } from "../../constructs/core";
|
|
4
4
|
export interface ErrorBudgetAlarmProps {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the SLO. Assumed to be unique per stack.
|
|
7
|
+
*/
|
|
5
8
|
sloName: string;
|
|
9
|
+
/**
|
|
10
|
+
* The percentange target for the SLO, expressed as a decimal floating point number. Must be between 0.95 and 0.995.
|
|
11
|
+
*/
|
|
6
12
|
sloTarget: number;
|
|
13
|
+
/**
|
|
14
|
+
* The metric representing the bad events. Can be any IMetric type, like Metric or MathExpression.
|
|
15
|
+
*/
|
|
7
16
|
badEvents: IMetric;
|
|
17
|
+
/**
|
|
18
|
+
* The metric representing the events to be considered. Can be any IMetric type, like Metric or MathExpression.
|
|
19
|
+
*/
|
|
8
20
|
validEvents: IMetric;
|
|
21
|
+
/**
|
|
22
|
+
* The sns topic name (not the full arn) used by the alarm to send email alerts.
|
|
23
|
+
*/
|
|
9
24
|
snsTopicNameForAlerts: string;
|
|
10
25
|
}
|
|
11
26
|
/**
|
|
@@ -32,6 +47,9 @@ export interface ErrorBudgetAlarmProps {
|
|
|
32
47
|
* The composite alarms have an awareness of priority, meaning that low priority alarm notifications are suppressed if
|
|
33
48
|
* a higher priority alarm is already firing. That is, if you receive an alert about fast error budget consumption, you
|
|
34
49
|
* should not receive an alert from the medium or slow versions of the alarm.
|
|
50
|
+
*
|
|
51
|
+
* For how to instantiate an ErrorBudgetAlarm check the unit-test in error-budget-alarm.test.ts,
|
|
52
|
+
* and for a description of the props that need to be passed see [[ErrorBudgetAlarmProps]]
|
|
35
53
|
*/
|
|
36
54
|
export declare class GuErrorBudgetAlarmExperimental extends Construct {
|
|
37
55
|
constructor(scope: GuStack, props: ErrorBudgetAlarmProps);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-budget-alarm.d.ts","sourceRoot":"","sources":["../../../src/experimental/constructs/error-budget-alarm.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAa7E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AA+BD
|
|
1
|
+
{"version":3,"file":"error-budget-alarm.d.ts","sourceRoot":"","sources":["../../../src/experimental/constructs/error-budget-alarm.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAa7E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AA+BD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,8BAA+B,SAAQ,SAAS;gBAC/C,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,qBAAqB;CAsEzD"}
|
|
@@ -30,9 +30,17 @@ const constructs_1 = require("constructs");
|
|
|
30
30
|
* The composite alarms have an awareness of priority, meaning that low priority alarm notifications are suppressed if
|
|
31
31
|
* a higher priority alarm is already firing. That is, if you receive an alert about fast error budget consumption, you
|
|
32
32
|
* should not receive an alert from the medium or slow versions of the alarm.
|
|
33
|
+
*
|
|
34
|
+
* For how to instantiate an ErrorBudgetAlarm check the unit-test in error-budget-alarm.test.ts,
|
|
35
|
+
* and for a description of the props that need to be passed see [[ErrorBudgetAlarmProps]]
|
|
33
36
|
*/
|
|
34
37
|
class GuErrorBudgetAlarmExperimental extends constructs_1.Construct {
|
|
35
38
|
constructor(scope, props) {
|
|
39
|
+
const lowestTargetAllowed = 0.95;
|
|
40
|
+
const highestTargetAllowed = 0.9995;
|
|
41
|
+
if (!(props.sloTarget >= lowestTargetAllowed && props.sloTarget <= highestTargetAllowed)) {
|
|
42
|
+
throw new Error(`ErrorBudgetAlarm only works with SLO targets between ${lowestTargetAllowed} and ${highestTargetAllowed}`);
|
|
43
|
+
}
|
|
36
44
|
super(scope, props.sloName); // The assumption here is that `sloName` will be unique (per stack)
|
|
37
45
|
const errorBudget = 1 - props.sloTarget;
|
|
38
46
|
const snsTopic = aws_sns_1.Topic.fromTopicArn(scope, `SnsSloAlarmsFor${props.sloName}`, `arn:aws:sns:${scope.region}:${scope.account}:${props.snsTopicNameForAlerts}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-budget-alarm.js","sourceRoot":"","sources":["../../../src/experimental/constructs/error-budget-alarm.ts"],"names":[],"mappings":"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","sourcesContent":["import { Duration } from \"aws-cdk-lib\";\nimport type { CfnCompositeAlarm, IMetric } from \"aws-cdk-lib/aws-cloudwatch\";\nimport {\n Alarm,\n AlarmRule,\n AlarmState,\n ComparisonOperator,\n CompositeAlarm,\n MathExpression,\n TreatMissingData,\n} from \"aws-cdk-lib/aws-cloudwatch\";\nimport { SnsAction } from \"aws-cdk-lib/aws-cloudwatch-actions\";\nimport type { ITopic } from \"aws-cdk-lib/aws-sns\";\nimport { Topic } from \"aws-cdk-lib/aws-sns\";\nimport { Construct } from \"constructs\";\nimport type { GuStack } from \"../../constructs/core\";\n\nexport interface ErrorBudgetAlarmProps {\n sloName: string;\n sloTarget: number;\n badEvents: IMetric;\n validEvents: IMetric;\n snsTopicNameForAlerts: string;\n}\n\ninterface BurnRate {\n speed: \"Fast\" | \"Medium\" | \"Slow\";\n burnRate: number;\n}\n\ninterface BurnRateMonitoring extends BurnRate {\n longPeriod: Duration;\n shortPeriod: Duration;\n}\n\ninterface MonitorBurnRateForPeriodProps {\n alarmName: string;\n burnRateSpeed: BurnRate;\n period: Duration;\n errorBudget: number;\n badEvents: IMetric;\n validEvents: IMetric;\n}\n\ninterface CompositeBurnRateAlarmProps {\n sloName: string;\n burnRateMonitoring: BurnRateMonitoring;\n errorBudget: number;\n badEvents: IMetric;\n validEvents: IMetric;\n snsTopic: ITopic;\n suppressorAlarm?: CompositeBurnRateAlarm;\n}\n\n/**\n * Use this construct to create alarms that will notify you when you are at risk of missing your SLO targets.\n *\n * These alarms follow the recommendations in the [SRE Workbook](https://sre.google/workbook/alerting-on-slos/) to\n * implement Multiwindow, Multi-Burn-Rate Alerts.\n *\n * The CloudWatch implementation of this strategy creates nine separate CloudWatch alarms. The main alarms that users\n * need to care about are the three composite alarms. These track different burn rates of error budget consumption:\n * fast (e.g. complete outages), medium (e.g. moderate error percentage over hours, rather than minutes) and slow (e.g.\n * a small percentage of errors sustained over a much longer period). Multiple burn rates are used to ensure that\n * we are able to pick up all scenarios which could threaten the SLO, without interrupting developers with an\n * unnecessary level of urgency.\n *\n * Each composite alarm creates two child alarms; these are used to support multiple windows (or periods in CloudWatch\n * terminology). A composite alarm is configured to send notifications if both of its child alarms are in an alarm state.\n * The child alarms themselves have no actions/notifications configured and users should not need to interact with them\n * directly. Multiple windows are helpful for accuracy and reset time. The long window helps us to avoid sending an\n * alert in scenarios where no intervention is required, for example a very brief spike in errors that is resolved\n * automatically (e.g. when AWS replaces a 'bad' EC2 instance). The short window helps to ensure that the alert is reset\n * (or moved back into OK state, in CloudWatch terminology) promptly once a problem has been resolved.\n *\n * The composite alarms have an awareness of priority, meaning that low priority alarm notifications are suppressed if\n * a higher priority alarm is already firing. That is, if you receive an alert about fast error budget consumption, you\n * should not receive an alert from the medium or slow versions of the alarm.\n */\nexport class GuErrorBudgetAlarmExperimental extends Construct {\n constructor(scope: GuStack, props: ErrorBudgetAlarmProps) {\n super(scope, props.sloName); // The assumption here is that `sloName` will be unique (per stack)\n\n const errorBudget = 1 - props.sloTarget;\n\n const snsTopic: ITopic = Topic.fromTopicArn(\n scope,\n `SnsSloAlarmsFor${props.sloName}`,\n `arn:aws:sns:${scope.region}:${scope.account}:${props.snsTopicNameForAlerts}`\n );\n\n const fastBurnRate: BurnRateMonitoring = {\n speed: \"Fast\",\n burnRate: 14.4,\n longPeriod: Duration.hours(1),\n shortPeriod: Duration.minutes(5),\n };\n\n const fastBurnAlarm = new CompositeBurnRateAlarm(scope, {\n sloName: props.sloName,\n badEvents: props.badEvents,\n burnRateMonitoring: fastBurnRate,\n errorBudget,\n snsTopic,\n validEvents: props.validEvents,\n });\n\n const mediumBurnRateMonitoring: BurnRateMonitoring = {\n speed: \"Medium\",\n burnRate: 6,\n longPeriod: Duration.hours(6),\n shortPeriod: Duration.minutes(30),\n };\n\n const mediumBurnRateAlarm = new CompositeBurnRateAlarm(scope, {\n sloName: props.sloName,\n badEvents: props.badEvents,\n burnRateMonitoring: mediumBurnRateMonitoring,\n errorBudget,\n snsTopic,\n validEvents: props.validEvents,\n suppressorAlarm: fastBurnAlarm,\n });\n\n const slowBurnRate: BurnRateMonitoring = {\n speed: \"Slow\",\n burnRate: 3,\n longPeriod: Duration.days(1),\n shortPeriod: Duration.hours(2),\n };\n\n new CompositeBurnRateAlarm(scope, {\n sloName: props.sloName,\n badEvents: props.badEvents,\n burnRateMonitoring: slowBurnRate,\n errorBudget,\n // TODO - use a different topic for tickets or figure out how to route notifications vs tickets using a single topic\n snsTopic,\n validEvents: props.validEvents,\n suppressorAlarm: mediumBurnRateAlarm,\n });\n }\n}\n\nclass MonitorBurnRateForPeriod extends Alarm {\n constructor(scope: GuStack, props: MonitorBurnRateForPeriodProps) {\n const undesirableErrorBudgetConsumption = props.errorBudget * props.burnRateSpeed.burnRate;\n super(scope, props.alarmName, {\n alarmName: props.alarmName,\n metric: new MathExpression({\n expression: \"badEvents/validEvents\",\n label: \"Observed failure rate\",\n usingMetrics: {\n badEvents: props.badEvents,\n validEvents: props.validEvents,\n },\n period: props.period,\n }),\n evaluationPeriods: 1,\n threshold: undesirableErrorBudgetConsumption,\n comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,\n treatMissingData: TreatMissingData.NOT_BREACHING,\n });\n }\n}\n\nclass CompositeBurnRateAlarm extends CompositeAlarm {\n constructor(scope: GuStack, props: CompositeBurnRateAlarmProps) {\n const alarmName = `${props.sloName}${props.burnRateMonitoring.speed}BurnCompositeAlarm`;\n const sharedIndividualAlarmProps = {\n badEvents: props.badEvents,\n burnRateSpeed: {\n speed: props.burnRateMonitoring.speed,\n burnRate: props.burnRateMonitoring.burnRate,\n },\n errorBudget: props.errorBudget,\n validEvents: props.validEvents,\n };\n super(scope, alarmName, {\n alarmRule: AlarmRule.allOf(\n AlarmRule.fromAlarm(\n new MonitorBurnRateForPeriod(scope, {\n ...sharedIndividualAlarmProps,\n alarmName: `ChildAlarmLongPeriod${alarmName}`,\n period: props.burnRateMonitoring.longPeriod,\n }),\n AlarmState.ALARM\n ),\n AlarmRule.fromAlarm(\n new MonitorBurnRateForPeriod(scope, {\n ...sharedIndividualAlarmProps,\n alarmName: `ChildAlarmShortPeriod${alarmName}`,\n period: props.burnRateMonitoring.shortPeriod,\n }),\n AlarmState.ALARM\n )\n ),\n });\n this.addAlarmAction(new SnsAction(props.snsTopic));\n if (props.suppressorAlarm) {\n const cfnAlarm = this.node.defaultChild as CfnCompositeAlarm;\n cfnAlarm.actionsSuppressor = props.suppressorAlarm.alarmArn;\n cfnAlarm.actionsSuppressorWaitPeriod = 120;\n cfnAlarm.actionsSuppressorExtensionPeriod = props.burnRateMonitoring.shortPeriod.toSeconds();\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"error-budget-alarm.js","sourceRoot":"","sources":["../../../src/experimental/constructs/error-budget-alarm.ts"],"names":[],"mappings":"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","sourcesContent":["import { Duration } from \"aws-cdk-lib\";\nimport type { CfnCompositeAlarm, IMetric } from \"aws-cdk-lib/aws-cloudwatch\";\nimport {\n Alarm,\n AlarmRule,\n AlarmState,\n ComparisonOperator,\n CompositeAlarm,\n MathExpression,\n TreatMissingData,\n} from \"aws-cdk-lib/aws-cloudwatch\";\nimport { SnsAction } from \"aws-cdk-lib/aws-cloudwatch-actions\";\nimport type { ITopic } from \"aws-cdk-lib/aws-sns\";\nimport { Topic } from \"aws-cdk-lib/aws-sns\";\nimport { Construct } from \"constructs\";\nimport type { GuStack } from \"../../constructs/core\";\n\nexport interface ErrorBudgetAlarmProps {\n /**\n * The name of the SLO. Assumed to be unique per stack.\n */\n sloName: string;\n /**\n * The percentange target for the SLO, expressed as a decimal floating point number. Must be between 0.95 and 0.995.\n */\n sloTarget: number;\n /**\n * The metric representing the bad events. Can be any IMetric type, like Metric or MathExpression.\n */\n badEvents: IMetric;\n /**\n * The metric representing the events to be considered. Can be any IMetric type, like Metric or MathExpression.\n */\n validEvents: IMetric;\n /**\n * The sns topic name (not the full arn) used by the alarm to send email alerts.\n */\n snsTopicNameForAlerts: string;\n}\n\ninterface BurnRate {\n speed: \"Fast\" | \"Medium\" | \"Slow\";\n burnRate: number;\n}\n\ninterface BurnRateMonitoring extends BurnRate {\n longPeriod: Duration;\n shortPeriod: Duration;\n}\n\ninterface MonitorBurnRateForPeriodProps {\n alarmName: string;\n burnRateSpeed: BurnRate;\n period: Duration;\n errorBudget: number;\n badEvents: IMetric;\n validEvents: IMetric;\n}\n\ninterface CompositeBurnRateAlarmProps {\n sloName: string;\n burnRateMonitoring: BurnRateMonitoring;\n errorBudget: number;\n badEvents: IMetric;\n validEvents: IMetric;\n snsTopic: ITopic;\n suppressorAlarm?: CompositeBurnRateAlarm;\n}\n\n/**\n * Use this construct to create alarms that will notify you when you are at risk of missing your SLO targets.\n *\n * These alarms follow the recommendations in the [SRE Workbook](https://sre.google/workbook/alerting-on-slos/) to\n * implement Multiwindow, Multi-Burn-Rate Alerts.\n *\n * The CloudWatch implementation of this strategy creates nine separate CloudWatch alarms. The main alarms that users\n * need to care about are the three composite alarms. These track different burn rates of error budget consumption:\n * fast (e.g. complete outages), medium (e.g. moderate error percentage over hours, rather than minutes) and slow (e.g.\n * a small percentage of errors sustained over a much longer period). Multiple burn rates are used to ensure that\n * we are able to pick up all scenarios which could threaten the SLO, without interrupting developers with an\n * unnecessary level of urgency.\n *\n * Each composite alarm creates two child alarms; these are used to support multiple windows (or periods in CloudWatch\n * terminology). A composite alarm is configured to send notifications if both of its child alarms are in an alarm state.\n * The child alarms themselves have no actions/notifications configured and users should not need to interact with them\n * directly. Multiple windows are helpful for accuracy and reset time. The long window helps us to avoid sending an\n * alert in scenarios where no intervention is required, for example a very brief spike in errors that is resolved\n * automatically (e.g. when AWS replaces a 'bad' EC2 instance). The short window helps to ensure that the alert is reset\n * (or moved back into OK state, in CloudWatch terminology) promptly once a problem has been resolved.\n *\n * The composite alarms have an awareness of priority, meaning that low priority alarm notifications are suppressed if\n * a higher priority alarm is already firing. That is, if you receive an alert about fast error budget consumption, you\n * should not receive an alert from the medium or slow versions of the alarm.\n *\n * For how to instantiate an ErrorBudgetAlarm check the unit-test in error-budget-alarm.test.ts,\n * and for a description of the props that need to be passed see [[ErrorBudgetAlarmProps]]\n */\nexport class GuErrorBudgetAlarmExperimental extends Construct {\n constructor(scope: GuStack, props: ErrorBudgetAlarmProps) {\n const lowestTargetAllowed = 0.95;\n const highestTargetAllowed = 0.9995;\n if (!(props.sloTarget >= lowestTargetAllowed && props.sloTarget <= highestTargetAllowed)) {\n throw new Error(\n `ErrorBudgetAlarm only works with SLO targets between ${lowestTargetAllowed} and ${highestTargetAllowed}`\n );\n }\n\n super(scope, props.sloName); // The assumption here is that `sloName` will be unique (per stack)\n\n const errorBudget = 1 - props.sloTarget;\n\n const snsTopic: ITopic = Topic.fromTopicArn(\n scope,\n `SnsSloAlarmsFor${props.sloName}`,\n `arn:aws:sns:${scope.region}:${scope.account}:${props.snsTopicNameForAlerts}`\n );\n\n const fastBurnRate: BurnRateMonitoring = {\n speed: \"Fast\",\n burnRate: 14.4,\n longPeriod: Duration.hours(1),\n shortPeriod: Duration.minutes(5),\n };\n\n const fastBurnAlarm = new CompositeBurnRateAlarm(scope, {\n sloName: props.sloName,\n badEvents: props.badEvents,\n burnRateMonitoring: fastBurnRate,\n errorBudget,\n snsTopic,\n validEvents: props.validEvents,\n });\n\n const mediumBurnRateMonitoring: BurnRateMonitoring = {\n speed: \"Medium\",\n burnRate: 6,\n longPeriod: Duration.hours(6),\n shortPeriod: Duration.minutes(30),\n };\n\n const mediumBurnRateAlarm = new CompositeBurnRateAlarm(scope, {\n sloName: props.sloName,\n badEvents: props.badEvents,\n burnRateMonitoring: mediumBurnRateMonitoring,\n errorBudget,\n snsTopic,\n validEvents: props.validEvents,\n suppressorAlarm: fastBurnAlarm,\n });\n\n const slowBurnRate: BurnRateMonitoring = {\n speed: \"Slow\",\n burnRate: 3,\n longPeriod: Duration.days(1),\n shortPeriod: Duration.hours(2),\n };\n\n new CompositeBurnRateAlarm(scope, {\n sloName: props.sloName,\n badEvents: props.badEvents,\n burnRateMonitoring: slowBurnRate,\n errorBudget,\n // TODO - use a different topic for tickets or figure out how to route notifications vs tickets using a single topic\n snsTopic,\n validEvents: props.validEvents,\n suppressorAlarm: mediumBurnRateAlarm,\n });\n }\n}\n\nclass MonitorBurnRateForPeriod extends Alarm {\n constructor(scope: GuStack, props: MonitorBurnRateForPeriodProps) {\n const undesirableErrorBudgetConsumption = props.errorBudget * props.burnRateSpeed.burnRate;\n super(scope, props.alarmName, {\n alarmName: props.alarmName,\n metric: new MathExpression({\n expression: \"badEvents/validEvents\",\n label: \"Observed failure rate\",\n usingMetrics: {\n badEvents: props.badEvents,\n validEvents: props.validEvents,\n },\n period: props.period,\n }),\n evaluationPeriods: 1,\n threshold: undesirableErrorBudgetConsumption,\n comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,\n treatMissingData: TreatMissingData.NOT_BREACHING,\n });\n }\n}\n\nclass CompositeBurnRateAlarm extends CompositeAlarm {\n constructor(scope: GuStack, props: CompositeBurnRateAlarmProps) {\n const alarmName = `${props.sloName}${props.burnRateMonitoring.speed}BurnCompositeAlarm`;\n const sharedIndividualAlarmProps = {\n badEvents: props.badEvents,\n burnRateSpeed: {\n speed: props.burnRateMonitoring.speed,\n burnRate: props.burnRateMonitoring.burnRate,\n },\n errorBudget: props.errorBudget,\n validEvents: props.validEvents,\n };\n super(scope, alarmName, {\n alarmRule: AlarmRule.allOf(\n AlarmRule.fromAlarm(\n new MonitorBurnRateForPeriod(scope, {\n ...sharedIndividualAlarmProps,\n alarmName: `ChildAlarmLongPeriod${alarmName}`,\n period: props.burnRateMonitoring.longPeriod,\n }),\n AlarmState.ALARM\n ),\n AlarmRule.fromAlarm(\n new MonitorBurnRateForPeriod(scope, {\n ...sharedIndividualAlarmProps,\n alarmName: `ChildAlarmShortPeriod${alarmName}`,\n period: props.burnRateMonitoring.shortPeriod,\n }),\n AlarmState.ALARM\n )\n ),\n });\n this.addAlarmAction(new SnsAction(props.snsTopic));\n if (props.suppressorAlarm) {\n const cfnAlarm = this.node.defaultChild as CfnCompositeAlarm;\n cfnAlarm.actionsSuppressor = props.suppressorAlarm.alarmArn;\n cfnAlarm.actionsSuppressorWaitPeriod = 120;\n cfnAlarm.actionsSuppressorExtensionPeriod = props.burnRateMonitoring.shortPeriod.toSeconds();\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/experimental/constructs/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./error-budget-alarm"), exports);
|
|
18
|
+
__exportStar(require("./root"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/experimental/constructs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uDAAqC;AACrC,yCAAuB","sourcesContent":["export * from \"./error-budget-alarm\";\nexport * from \"./root\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guardian/cdk",
|
|
3
3
|
"description": "Generic Guardian flavoured AWS CDK components",
|
|
4
|
-
"version": "48.
|
|
4
|
+
"version": "48.3.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"@types/lodash.camelcase": "^4.3.7",
|
|
36
36
|
"@types/lodash.kebabcase": "^4.1.7",
|
|
37
37
|
"@types/lodash.upperfirst": "^4.3.7",
|
|
38
|
-
"@types/node": "18.11.
|
|
38
|
+
"@types/node": "18.11.9",
|
|
39
39
|
"@types/yargs": "^17.0.13",
|
|
40
40
|
"aws-cdk": "2.45.0",
|
|
41
41
|
"cz-conventional-changelog": "^3.3.0",
|
|
42
|
-
"eslint": "^8.
|
|
42
|
+
"eslint": "^8.26.0",
|
|
43
43
|
"eslint-plugin-custom-rules": "file:tools/eslint",
|
|
44
44
|
"gh-pages": "^4.0.0",
|
|
45
45
|
"jest": "^29.2.1",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"serve": "^14.0.1",
|
|
49
49
|
"ts-jest": "^29.0.3",
|
|
50
50
|
"ts-node": "^10.9.1",
|
|
51
|
-
"typedoc": "^0.23.
|
|
51
|
+
"typedoc": "^0.23.19",
|
|
52
52
|
"typescript": "~4.8.4"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@oclif/core": "1.
|
|
55
|
+
"@oclif/core": "1.20.2",
|
|
56
56
|
"aws-cdk-lib": "2.45.0",
|
|
57
|
-
"aws-sdk": "^2.
|
|
57
|
+
"aws-sdk": "^2.1245.0",
|
|
58
58
|
"chalk": "^4.1.2",
|
|
59
59
|
"codemaker": "^1.70.0",
|
|
60
60
|
"constructs": "10.1.127",
|