From f784161edbc0772559edbda230831f5f80cf19d6 Mon Sep 17 00:00:00 2001 From: Tanner Lewis Date: Thu, 30 Mar 2023 15:26:59 -0400 Subject: [PATCH] Add VPC support through separate CDK Stack (#2) MIGRATIONS-1014: Add optional VPC stack with unit tests and updated doc Signed-off-by: Tanner Lewis --- deployment/cdk/opensearch-service/README.md | 4 +-- deployment/cdk/opensearch-service/bin/app.ts | 6 ++++ .../opensearch-service/default-values.json | 2 +- .../opensearch-service/lib/network-stack.ts | 3 +- .../opensearch-service-domain-cdk-stack.ts | 3 +- .../opensearch-service/lib/stack-composer.ts | 8 ++++-- .../test/domain-cdk-stack.test.ts | 18 ++++++------ .../test/network-stack.test.ts | 4 +-- .../test/stack-composer.test.ts | 28 +++++++++---------- 9 files changed, 44 insertions(+), 32 deletions(-) diff --git a/deployment/cdk/opensearch-service/README.md b/deployment/cdk/opensearch-service/README.md index e57a0fc50..b0636e112 100644 --- a/deployment/cdk/opensearch-service/README.md +++ b/deployment/cdk/opensearch-service/README.md @@ -27,10 +27,10 @@ Depending on your use-case, you may choose to provide options from both the `cdk 3. Existing `default-values.json` in the same directory as this README ### Stack Breakdown -This CDK has been structured to allow multiple stacks to be deployed natively, which allows an easy entrance door for user stacks to be added. At a minimum the Domain stack will be deployed, with further explanation of the possible native stacks below +This CDK has been structured to allow multiple stacks to be deployed out-of-the-box, which allows an easy entrance door for users to get started and add additional stacks as they need. Each of these stacks are deployed independently in CloudFormation, with only the Domain stack being required. #### Domain Stack (OSServiceDomainCDKStack-STAGE-REGION) -This is the core stack of this CDK which is responsible for deploying the OpenSearch Service Domain and associated resources such as CloudWatch log groups for Domain logging. +This is the core required stack of this CDK which is responsible for deploying the OpenSearch Service Domain and associated resources such as CloudWatch log groups for Domain logging. #### Network Stack (OSServiceNetworkCDKStack-STAGE-REGION) This is an additional stack that will be used when the Domain is configured to be placed inside a VPC and will contain resources related to the networking of this VPC such as Security Groups and Subnets. diff --git a/deployment/cdk/opensearch-service/bin/app.ts b/deployment/cdk/opensearch-service/bin/app.ts index efba08c7e..d7199924e 100644 --- a/deployment/cdk/opensearch-service/bin/app.ts +++ b/deployment/cdk/opensearch-service/bin/app.ts @@ -6,6 +6,12 @@ import {StackComposer} from "../lib/stack-composer"; const app = new App(); const account = process.env.CDK_DEFAULT_ACCOUNT const region = process.env.CDK_DEFAULT_REGION +const stage = process.env.CDK_DEPLOYMENT_STAGE +if (!stage) { + throw new Error("Required environment variable CDK_DEPLOYMENT_STAGE has not been set (i.e. dev, gamma, PROD)") +} + new StackComposer(app, { env: { account: account, region: region }, + stage: stage }); \ No newline at end of file diff --git a/deployment/cdk/opensearch-service/default-values.json b/deployment/cdk/opensearch-service/default-values.json index 539873991..238ba01d7 100644 --- a/deployment/cdk/opensearch-service/default-values.json +++ b/deployment/cdk/opensearch-service/default-values.json @@ -1,4 +1,4 @@ { - "engineVersion": "OS_1.3", + "engineVersion": "OS_2.5", "domainName": "os-service-domain" } \ No newline at end of file diff --git a/deployment/cdk/opensearch-service/lib/network-stack.ts b/deployment/cdk/opensearch-service/lib/network-stack.ts index a80de9b5b..150169e78 100644 --- a/deployment/cdk/opensearch-service/lib/network-stack.ts +++ b/deployment/cdk/opensearch-service/lib/network-stack.ts @@ -15,8 +15,9 @@ import { Vpc } from "aws-cdk-lib/aws-ec2"; import {Construct} from "constructs"; +import {StackPropsExt} from "./stack-composer"; -export interface networkStackProps extends StackProps { +export interface networkStackProps extends StackPropsExt { readonly vpcId?: string readonly vpcSubnetIds?: string[] readonly vpcSecurityGroupIds?: string[] diff --git a/deployment/cdk/opensearch-service/lib/opensearch-service-domain-cdk-stack.ts b/deployment/cdk/opensearch-service/lib/opensearch-service-domain-cdk-stack.ts index 58fc58364..f6a70e4e5 100644 --- a/deployment/cdk/opensearch-service/lib/opensearch-service-domain-cdk-stack.ts +++ b/deployment/cdk/opensearch-service/lib/opensearch-service-domain-cdk-stack.ts @@ -9,9 +9,10 @@ import {IKey, Key} from "aws-cdk-lib/aws-kms"; import {PolicyStatement} from "aws-cdk-lib/aws-iam"; import {ILogGroup, LogGroup} from "aws-cdk-lib/aws-logs"; import {Secret} from "aws-cdk-lib/aws-secretsmanager"; +import {StackPropsExt} from "./stack-composer"; -export interface opensearchServiceDomainCdkProps extends StackProps{ +export interface opensearchServiceDomainCdkProps extends StackPropsExt { readonly version: EngineVersion, readonly domainName: string, readonly dataNodeInstanceType?: string, diff --git a/deployment/cdk/opensearch-service/lib/stack-composer.ts b/deployment/cdk/opensearch-service/lib/stack-composer.ts index 38aecb357..f471473ce 100644 --- a/deployment/cdk/opensearch-service/lib/stack-composer.ts +++ b/deployment/cdk/opensearch-service/lib/stack-composer.ts @@ -11,13 +11,17 @@ import * as defaultValuesJson from "../default-values.json" import {NetworkStack} from "./network-stack"; import {MigrationAssistanceStack} from "./migration-assistance-stack"; +export interface StackPropsExt extends StackProps { + readonly stage: string +} + export class StackComposer { public stacks: Stack[] = []; - constructor(scope: Construct, props: StackProps) { + constructor(scope: Construct, props: StackPropsExt) { let networkStack: NetworkStack|undefined - const stage = process.env.CDK_DEPLOYMENT_STAGE + const stage = props.stage const account = props.env?.account const region = props.env?.region diff --git a/deployment/cdk/opensearch-service/test/domain-cdk-stack.test.ts b/deployment/cdk/opensearch-service/test/domain-cdk-stack.test.ts index 016706575..8156c137b 100644 --- a/deployment/cdk/opensearch-service/test/domain-cdk-stack.test.ts +++ b/deployment/cdk/opensearch-service/test/domain-cdk-stack.test.ts @@ -48,7 +48,7 @@ test('Test primary context options are mapped with standard data type', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -96,7 +96,7 @@ test('Test primary context options are mapped with only string data type', () => }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -124,7 +124,7 @@ test('Test alternate context options are mapped with standard data type', () => }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -147,7 +147,7 @@ test('Test alternate context options are mapped with only string data type', () }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -163,7 +163,7 @@ test('Test openAccessPolicy setting creates access policy when enabled', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -181,7 +181,7 @@ test('Test openAccessPolicy setting does not create access policy when disabled' }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -199,7 +199,7 @@ test('Test openAccessPolicy setting is mapped with string data type', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -215,7 +215,7 @@ test( 'Test default stack is created with default values when no context options }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const defaultValues: { [x: string]: (string); } = testDefaultValues @@ -263,7 +263,7 @@ test( 'Test default stack is created when empty context options are provided for }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] diff --git a/deployment/cdk/opensearch-service/test/network-stack.test.ts b/deployment/cdk/opensearch-service/test/network-stack.test.ts index 6b369d142..6fed47234 100644 --- a/deployment/cdk/opensearch-service/test/network-stack.test.ts +++ b/deployment/cdk/opensearch-service/test/network-stack.test.ts @@ -11,7 +11,7 @@ test('Test vpcEnabled setting that is disabled does not create stack', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) openSearchStacks.stacks.forEach(function(stack) { @@ -30,7 +30,7 @@ test('Test vpcEnabled setting that is enabled without existing resources creates }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const networkStack: NetworkStack = (openSearchStacks.stacks.filter((s) => s instanceof NetworkStack)[0]) as NetworkStack diff --git a/deployment/cdk/opensearch-service/test/stack-composer.test.ts b/deployment/cdk/opensearch-service/test/stack-composer.test.ts index fe93895de..553ff17eb 100644 --- a/deployment/cdk/opensearch-service/test/stack-composer.test.ts +++ b/deployment/cdk/opensearch-service/test/stack-composer.test.ts @@ -12,7 +12,7 @@ test('Test missing domain name throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -27,7 +27,7 @@ test('Test missing engine version throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -43,7 +43,7 @@ test('Test invalid engine version format throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -58,7 +58,7 @@ test('Test ES 7.10 engine version format is parsed', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -75,7 +75,7 @@ test('Test OS 1.3 engine version format is parsed', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -108,7 +108,7 @@ test('Test access policy is parsed for proper array format', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -135,7 +135,7 @@ test('Test access policy is parsed for proper block format', () => { }) const openSearchStacks = new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) const domainStack = openSearchStacks.stacks.filter((s) => s instanceof OpensearchServiceDomainCdkStack)[0] @@ -153,7 +153,7 @@ test('Test access policy missing Statement throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -168,7 +168,7 @@ test('Test access policy with empty Statement array throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -183,7 +183,7 @@ test('Test access policy with empty Statement block throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -199,7 +199,7 @@ test('Test access policy with improper Statement throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -214,7 +214,7 @@ test('Test invalid TLS security policy throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -229,7 +229,7 @@ test('Test invalid EBS volume type throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError() @@ -244,7 +244,7 @@ test('Test invalid domain removal policy type throws error', () => { }) const createStackFunc = () => new StackComposer(app, { - env: {account: "test-account", region: "us-east-1"} + env: {account: "test-account", region: "us-east-1"}, stage: "unittest" }) expect(createStackFunc).toThrowError()