diff --git a/config/constants.ts b/config/constants.ts index 23541e1ff..6fe13d9ab 100644 --- a/config/constants.ts +++ b/config/constants.ts @@ -4,7 +4,7 @@ import { FilemanagerDependencies, OrcaBusStatelessConfig, } from '../lib/workload/orcabus-stateless-stack'; -import { Duration, aws_lambda, RemovalPolicy } from 'aws-cdk-lib'; +import { Duration, RemovalPolicy } from 'aws-cdk-lib'; import { EventSourceProps } from '../lib/workload/stateful/event_source/component'; import { DbAuthType } from '../lib/workload/stateless/postgres_manager/function/type'; @@ -68,8 +68,6 @@ const orcaBusStatelessConfig = { }, eventBusName: eventBusName, lambdaSecurityGroupName: lambdaSecurityGroupName, - lambdaRuntimePythonVersion: aws_lambda.Runtime.PYTHON_3_10, - bclConvertFunctionName: 'orcabus_bcl_convert', rdsMasterSecretName: rdsMasterSecretName, postgresManagerConfig: { masterSecretName: rdsMasterSecretName, diff --git a/config/param.ts b/config/param.ts new file mode 100644 index 000000000..c5f2c7e8d --- /dev/null +++ b/config/param.ts @@ -0,0 +1,3 @@ +// environmental neutral; global constants for SSM parameter store coordinate that share the +// contract between stateful and stateless workloads. These constants typically be ssm param. +export const SHARED_HTTP_API_ID: string = '/orcabus/shared-http-api-id'; diff --git a/lib/workload/orcabus-stateful-stack.ts b/lib/workload/orcabus-stateful-stack.ts index fb6ea1fc2..8262fafec 100644 --- a/lib/workload/orcabus-stateful-stack.ts +++ b/lib/workload/orcabus-stateful-stack.ts @@ -2,10 +2,11 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { getVpc } from './stateful/vpc/component'; import { EventBusConstruct, EventBusProps } from './stateful/eventbridge/component'; -import { Database, ConfigurableDatabaseProps } from './stateful/database/component'; +import { ConfigurableDatabaseProps, Database } from './stateful/database/component'; import { SecurityGroupConstruct, SecurityGroupProps } from './stateful/securitygroup/component'; import { SchemaRegistryConstruct, SchemaRegistryProps } from './stateful/schemaregistry/component'; import { EventSource, EventSourceProps } from './stateful/event_source/component'; +import { SharedApiGatewayConstruct } from './stateful/apigw/component'; export interface OrcaBusStatefulConfig { schemaRegistryProps: SchemaRegistryProps; @@ -55,5 +56,7 @@ export class OrcaBusStatefulStack extends cdk.Stack { if (props.eventSourceProps) { this.eventSource = new EventSource(this, 'EventSourceConstruct', props.eventSourceProps); } + + new SharedApiGatewayConstruct(this, 'OrcaBusSharedApiGatewayConstruct'); } } diff --git a/lib/workload/orcabus-stateless-stack.ts b/lib/workload/orcabus-stateless-stack.ts index a07f0cdec..8eef06fc5 100644 --- a/lib/workload/orcabus-stateless-stack.ts +++ b/lib/workload/orcabus-stateless-stack.ts @@ -1,5 +1,5 @@ import * as cdk from 'aws-cdk-lib'; -import { Arn, aws_lambda } from 'aws-cdk-lib'; +import { Arn, aws_ssm } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { getVpc } from './stateful/vpc/component'; import { MultiSchemaConstructProps } from './stateless/schema/component'; @@ -11,13 +11,15 @@ import { PostgresManagerStack, PostgresManagerConfig, } from './stateless/postgres_manager/deploy/postgres-manager-stack'; +import { SequenceRunManagerStack } from './stateless/sequence_run_manager/deploy/component'; +import { EventBus, IEventBus } from 'aws-cdk-lib/aws-events'; +import * as param from '../../config/param'; +import { IHttpApi, HttpApiAttributes, HttpStage, HttpApi } from 'aws-cdk-lib/aws-apigatewayv2'; export interface OrcaBusStatelessConfig { multiSchemaConstructProps: MultiSchemaConstructProps; eventBusName: string; lambdaSecurityGroupName: string; - lambdaRuntimePythonVersion: aws_lambda.Runtime; - bclConvertFunctionName: string; rdsMasterSecretName: string; postgresManagerConfig: PostgresManagerConfig; filemanagerDependencies?: FilemanagerDependencies; @@ -39,8 +41,10 @@ export interface FilemanagerDependencies { } export class OrcaBusStatelessStack extends cdk.Stack { - private vpc: IVpc; - private lambdaSecurityGroup: ISecurityGroup; + private readonly vpc: IVpc; + private readonly lambdaSecurityGroup: ISecurityGroup; + private readonly mainBus: IEventBus; + private readonly sharedHttpApi: IHttpApi; // microservice stacks microserviceStackArray: cdk.Stack[] = []; @@ -59,14 +63,26 @@ export class OrcaBusStatelessStack extends cdk.Stack { this.vpc ); - // const mainBus = EventBus.fromEventBusName(this, 'OrcaBusMain', props.eventBusName); + this.mainBus = EventBus.fromEventBusName(this, 'OrcaBusMain', props.eventBusName); + + // You may reuse the shared HttpApi Gateway if you prefer. Doing so, please use the namespace + // in the route path prefix. You can pass along `this.sharedHttpApi` through your stack props. + const sharedHttpApiId = aws_ssm.StringParameter.valueFromLookup(this, param.SHARED_HTTP_API_ID); + const sharedHttpApiAttributes: HttpApiAttributes = { httpApiId: sharedHttpApiId }; + this.sharedHttpApi = HttpApi.fromHttpApiAttributes( + this, + 'OrcaBusSharedHttpApi', + sharedHttpApiAttributes + ); + new HttpStage(this, 'OrcaBusSharedHttpApiStage', { httpApi: this.sharedHttpApi }); // --- Create Stateless resources // new MultiSchemaConstruct(this, 'MultiSchema', props.multiSchemaConstructProps); // hook microservice construct components here - this.createSequenceRunManager(); + + this.microserviceStackArray.push(this.createSequenceRunManager()); this.microserviceStackArray.push(this.createPostgresManager(props.postgresManagerConfig)); if (props.filemanagerDependencies) { @@ -78,8 +94,12 @@ export class OrcaBusStatelessStack extends cdk.Stack { } private createSequenceRunManager() { - // TODO new SequenceRunManagerConstruct() from lib/workload/stateless/sequence_run_manager/deploy/component.ts - // However, the implementation is still incomplete... + return new SequenceRunManagerStack(this, 'SequenceRunManager', { + securityGroups: [this.lambdaSecurityGroup], + vpc: this.vpc, + mainBus: this.mainBus, + httpApi: this.sharedHttpApi, + }); } private createPostgresManager(config: PostgresManagerConfig) { diff --git a/lib/workload/stateful/apigw/component.ts b/lib/workload/stateful/apigw/component.ts new file mode 100644 index 000000000..4fe58bfdf --- /dev/null +++ b/lib/workload/stateful/apigw/component.ts @@ -0,0 +1,42 @@ +import { Construct } from 'constructs'; +import { aws_ssm, Duration } from 'aws-cdk-lib'; +import * as cognito from 'aws-cdk-lib/aws-cognito'; +import { HttpUserPoolAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers'; +import { SHARED_HTTP_API_ID } from '../../../../config/param'; +import { CorsHttpMethod, HttpApi } from 'aws-cdk-lib/aws-apigatewayv2'; + +export class SharedApiGatewayConstruct extends Construct { + private readonly SSM_USER_POOL_ID: string = '/data_portal/client/cog_user_pool_id'; // FIXME one fine day in future + + constructor(scope: Construct, id: string) { + super(scope, id); + + const userPoolId: string = aws_ssm.StringParameter.valueFromLookup(this, this.SSM_USER_POOL_ID); + const userPool = cognito.UserPool.fromUserPoolId(this, id, userPoolId); + + const httpApi = new HttpApi(this, id + 'HttpApi', { + apiName: 'OrcaBusSharedAPI', + corsPreflight: { + allowHeaders: ['Authorization'], + allowMethods: [ + CorsHttpMethod.GET, + CorsHttpMethod.HEAD, + CorsHttpMethod.OPTIONS, + CorsHttpMethod.POST, + ], + allowOrigins: ['*'], // FIXME allowed origins from config constant + maxAge: Duration.days(10), + }, + defaultAuthorizer: new HttpUserPoolAuthorizer(id + 'HttpUserPoolAuthorizer', userPool), + // defaultDomainMapping: ... TODO + }); + + new aws_ssm.StringParameter(this, 'sharedHttpApiIdParameter', { + description: 'OrcaBus Shared API Gateway httpApiId', + parameterName: SHARED_HTTP_API_ID, + stringValue: httpApi.httpApiId, + }); + + // TODO setup cloud map service discovery perhaps + } +} diff --git a/lib/workload/stateless/sequence_run_manager/Makefile b/lib/workload/stateless/sequence_run_manager/Makefile index 355195f94..b4aa027ff 100644 --- a/lib/workload/stateless/sequence_run_manager/Makefile +++ b/lib/workload/stateless/sequence_run_manager/Makefile @@ -1,15 +1,15 @@ .PHONY: test suite install: - @pip install -r requirements-dev.txt + @pip install -r deps/requirements-dev.txt check: lint lint: - @black -t py312 --check ./src --exclude skel --exclude .venv + @black -t py312 --check . --exclude .venv lint-fix: - @black -t py312 ./src --exclude skel --exclude .venv + @black -t py312 . --exclude .venv # full mock suite test pipeline - install deps, bring up compose stack, run suite, bring down compose stack test: install up suite down diff --git a/lib/workload/stateless/sequence_run_manager/README.md b/lib/workload/stateless/sequence_run_manager/README.md index 6dd26e893..e3f6b54d3 100644 --- a/lib/workload/stateless/sequence_run_manager/README.md +++ b/lib/workload/stateless/sequence_run_manager/README.md @@ -18,8 +18,8 @@ _*If you are PyCharmer and opening the whole `orcabus` project (i.e. not doing s - Setup Python environment (conda or venv) ``` -conda create -n orcabus python=3.12 -conda activate orcabus +conda create -n sequence_run_manager python=3.12 +conda activate sequence_run_manager ``` ### Make diff --git a/lib/workload/stateless/sequence_run_manager/deploy/README.md b/lib/workload/stateless/sequence_run_manager/deploy/README.md index 96a6c87bf..cc89e7321 100644 --- a/lib/workload/stateless/sequence_run_manager/deploy/README.md +++ b/lib/workload/stateless/sequence_run_manager/deploy/README.md @@ -4,4 +4,4 @@ This directory contains CDK code constructs that will be called and assembled by You may have multi-level directory structure under this folder as see fit to arrange your CDK constructs. -However. Collectively, all CDK constructs created under this deploy directory will form as **one deployable component unit** for the higher level CDK Stack. Hence, just single `component.ts` file might be sufficed if your app deployment is a simpler CDK deployment construction. +However. Collectively, all CDK constructs created under this deploy directory will form as **one deployable stack** for the higher level CDK Stack. Hence, just single `stack-name.ts` file might be sufficed if your app deployment is a simpler CDK deployment construction. diff --git a/lib/workload/stateless/sequence_run_manager/deploy/component.ts b/lib/workload/stateless/sequence_run_manager/deploy/component.ts index 3f34ffbef..501b43e55 100644 --- a/lib/workload/stateless/sequence_run_manager/deploy/component.ts +++ b/lib/workload/stateless/sequence_run_manager/deploy/component.ts @@ -1,145 +1,123 @@ -// FIXME complete the implementation - import path from 'path'; import { Construct } from 'constructs'; -import { ILayerVersion } from 'aws-cdk-lib/aws-lambda'; import { ISecurityGroup, IVpc } from 'aws-cdk-lib/aws-ec2'; -import { IEventBus, Rule } from 'aws-cdk-lib/aws-events'; -import { aws_events_targets, aws_lambda, Duration } from 'aws-cdk-lib'; +import { IEventBus } from 'aws-cdk-lib/aws-events'; +import { aws_lambda, aws_secretsmanager, Stack } from 'aws-cdk-lib'; import { PythonFunction, PythonLayerVersion } from '@aws-cdk/aws-lambda-python-alpha'; -import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations-alpha'; -import { CorsHttpMethod, HttpApi, HttpMethod, HttpStage } from '@aws-cdk/aws-apigatewayv2-alpha'; +import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; +import { HttpMethod, HttpRoute, HttpRouteKey, IHttpApi } from 'aws-cdk-lib/aws-apigatewayv2'; +import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; export interface SequenceRunManagerProps { - // FIXME change prop interface name - layers: ILayerVersion[]; securityGroups: ISecurityGroup[]; vpc: IVpc; mainBus: IEventBus; - functionName: string; - lambdaRuntimePythonVersion: aws_lambda.Runtime; + httpApi: IHttpApi; } -export class SequenceRunManagerConstruct extends Construct { - // FIXME change construct name - private scope: Construct; +export class SequenceRunManagerStack extends Stack { + // Follow by naming convention. See https://github.com/umccr/orcabus/pull/149 + private readonly secretId: string = 'orcabus/sequence_run_manager/rdsLoginCredential'; + private readonly apiNamespace: string = '/srm/v1'; private readonly id: string; - private props: SequenceRunManagerProps; - private baseLayer: PythonLayerVersion; + private readonly props: SequenceRunManagerProps; + private readonly baseLayer: PythonLayerVersion; private readonly lambdaEnv; + private readonly lambdaRuntimePythonVersion: aws_lambda.Runtime = aws_lambda.Runtime.PYTHON_3_12; + private readonly lambdaRole: Role; constructor(scope: Construct, id: string, props: SequenceRunManagerProps) { super(scope, id); - this.scope = scope; this.id = id; this.props = props; + this.lambdaRole = new Role(this, this.id + 'Role', { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + description: 'Lambda execution role for ' + this.id, + }); + this.lambdaRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'), + ); + this.lambdaRole.addManagedPolicy( + ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'), + ); + + const dbSecret = aws_secretsmanager.Secret.fromSecretNameV2(this, this.id + 'dbSecret', this.secretId); + dbSecret.grantRead(this.lambdaRole); + this.lambdaEnv = { - DJANGO_SETTINGS_MODULE: 'sequence_run_manager.settings.aws', // FIXME project name + DJANGO_SETTINGS_MODULE: 'sequence_run_manager.settings.aws', EVENT_BUS_NAME: this.props.mainBus.eventBusName, + SECRET_ID: this.secretId, }; - this.createLambdaLayer(); + this.baseLayer = new PythonLayerVersion(this, this.id + 'Layer', { + entry: path.join(__dirname, '../deps'), + compatibleRuntimes: [this.lambdaRuntimePythonVersion], + }); + this.createMigrationHandler(); this.createApiHandler(); - this.createProcHandler(); this.createProcSqsHandler(); } - private createLambdaLayer() { - this.baseLayer = new PythonLayerVersion(this, this.id + 'Layer', { - entry: path.join(__dirname, '../'), // FIXME haven't tried whether dot dot slash here is working like this + private createPythonFunction(name: string, props: object): PythonFunction { + return new PythonFunction(this, this.id + name, { + entry: path.join(__dirname, '../'), + runtime: this.lambdaRuntimePythonVersion, + layers: [this.baseLayer], + environment: this.lambdaEnv, + securityGroups: this.props.securityGroups, + vpc: this.props.vpc, + vpcSubnets: { subnets: this.props.vpc.privateSubnets }, + role: this.lambdaRole, + ...props, }); } private createMigrationHandler() { - new PythonFunction(this, this.id + 'Migration', { - entry: path.join(__dirname, '../'), - runtime: this.props.lambdaRuntimePythonVersion, - layers: [this.baseLayer], + this.createPythonFunction('Migration', { index: 'migrate.py', handler: 'handler', - environment: this.lambdaEnv, }); } private createApiHandler() { - const apiFn = new PythonFunction(this, this.id + 'Api', { - entry: path.join(__dirname, '../'), - runtime: this.props.lambdaRuntimePythonVersion, - layers: [this.baseLayer], + const apiFn = this.createPythonFunction('Api', { index: 'api.py', handler: 'handler', - environment: this.lambdaEnv, }); const apiIntegration = new HttpLambdaIntegration(this.id + 'ApiIntegration', apiFn); - - const httpApi = new HttpApi(this, this.id + 'HttpApi', { - corsPreflight: { - allowHeaders: ['Authorization'], - allowMethods: [ - CorsHttpMethod.GET, - CorsHttpMethod.HEAD, - CorsHttpMethod.OPTIONS, - CorsHttpMethod.POST, - ], - allowOrigins: ['*'], // TODO to get this allowed origins from config constant - maxAge: Duration.days(10), - }, - }); - - httpApi.addRoutes({ - path: '/{proxy+}', - methods: [HttpMethod.ANY], + new HttpRoute(this, 'OrcaBusSRMHttpRoute', { + httpApi: this.props.httpApi, integration: apiIntegration, + routeKey: HttpRouteKey.with(this.apiNamespace + '/{proxy+}', HttpMethod.ANY), }); - - new HttpStage(this, this.id + 'ApiStage', { - httpApi: httpApi, - }); - } - - private createProcHandler() { - const procFn = new PythonFunction(this, this.id + 'ProcHandler', { - entry: path.join(__dirname, '../'), - runtime: this.props.lambdaRuntimePythonVersion, - layers: [this.baseLayer], - index: '{{project_name}}_proc/lambdas/hello_proc.py', // FIXME update appropriate path to Lambda entry point - handler: 'handler', - environment: this.lambdaEnv, - }); - - this.props.mainBus.grantPutEventsTo(procFn); - this.setupEventRule(procFn); } private createProcSqsHandler() { - const procSqsFn = new PythonFunction(this, this.id + 'ProcHandler', { - entry: path.join(__dirname, '../'), - runtime: this.props.lambdaRuntimePythonVersion, - layers: [this.baseLayer], - index: '{{project_name}}_proc/lambdas/hello_proc.py', // FIXME update appropriate path to Lambda entry point + const procSqsFn = this.createPythonFunction('ProcHandler', { + index: 'sequence_run_manager_proc/lambdas/bssh_event.py', handler: 'sqs_handler', - environment: this.lambdaEnv, }); - this.props.mainBus.grantPutEventsTo(procSqsFn); // FIXME remove this if no use - this.setupEventRule(procSqsFn); // FIXME remove this if no use + this.props.mainBus.grantPutEventsTo(procSqsFn); + // this.setupEventRule(procSqsFn); // TODO comment this out for now } - private setupEventRule(fn: aws_lambda.Function) { - const eventRule = new Rule(this, this.id + 'EventRule', { - ruleName: this.id + 'EventRule', - description: 'Rule to send {event_type.value} events to the {handler.function_name} Lambda', - eventBus: this.props.mainBus, - }); - - eventRule.addTarget(new aws_events_targets.LambdaFunction(fn)); - eventRule.addEventPattern({ - source: ['ORCHESTRATOR'], // FIXME complete source to destination event mapping - detailType: ['SequenceRunStateChange'], - }); - } + // private setupEventRule(fn: aws_lambda.Function) { + // const eventRule = new Rule(this, this.id + 'EventRule', { + // ruleName: this.id + 'EventRule', + // description: 'Rule to send {event_type.value} events to the {handler.function_name} Lambda', + // eventBus: this.props.mainBus, + // }); + // + // eventRule.addTarget(new aws_events_targets.LambdaFunction(fn)); + // eventRule.addEventPattern({ + // source: ['ORCHESTRATOR'], // FIXME complete source to destination event mapping + // detailType: ['SequenceRunStateChange'], + // }); + // } } diff --git a/lib/workload/stateless/sequence_run_manager/requirements-dev.txt b/lib/workload/stateless/sequence_run_manager/deps/requirements-dev.txt similarity index 100% rename from lib/workload/stateless/sequence_run_manager/requirements-dev.txt rename to lib/workload/stateless/sequence_run_manager/deps/requirements-dev.txt diff --git a/lib/workload/stateless/sequence_run_manager/requirements-test.txt b/lib/workload/stateless/sequence_run_manager/deps/requirements-test.txt similarity index 100% rename from lib/workload/stateless/sequence_run_manager/requirements-test.txt rename to lib/workload/stateless/sequence_run_manager/deps/requirements-test.txt diff --git a/lib/workload/stateless/sequence_run_manager/requirements.txt b/lib/workload/stateless/sequence_run_manager/deps/requirements.txt similarity index 97% rename from lib/workload/stateless/sequence_run_manager/requirements.txt rename to lib/workload/stateless/sequence_run_manager/deps/requirements.txt index c2ea20f6c..158563231 100644 --- a/lib/workload/stateless/sequence_run_manager/requirements.txt +++ b/lib/workload/stateless/sequence_run_manager/deps/requirements.txt @@ -15,4 +15,3 @@ serverless-wsgi==3.0.2 # for sequencerunstatechange package six==1.16.0 regex==2023.6.3 -pymysql==1.1.0 diff --git a/lib/workload/stateless/sequence_run_manager/sequence_run_manager/__init__.py b/lib/workload/stateless/sequence_run_manager/sequence_run_manager/__init__.py index 1e790e4fa..e69de29bb 100644 --- a/lib/workload/stateless/sequence_run_manager/sequence_run_manager/__init__.py +++ b/lib/workload/stateless/sequence_run_manager/sequence_run_manager/__init__.py @@ -1,5 +0,0 @@ -import pymysql - -# see https://github.com/PyMySQL/PyMySQL/issues/790 -pymysql.version_info = (1, 4, 6, "final", 0) -pymysql.install_as_MySQLdb() diff --git a/lib/workload/stateless/sequence_run_manager/sequence_run_manager/settings/aws.py b/lib/workload/stateless/sequence_run_manager/sequence_run_manager/settings/aws.py index adc919edc..6cb06bfaa 100644 --- a/lib/workload/stateless/sequence_run_manager/sequence_run_manager/settings/aws.py +++ b/lib/workload/stateless/sequence_run_manager/sequence_run_manager/settings/aws.py @@ -5,17 +5,30 @@ - export DJANGO_SETTINGS_MODULE=sequence_run_manager.settings.aws """ import copy +import json +import logging +from types import SimpleNamespace from environ import Env -from libumccr.aws import libssm +from libumccr.aws import libsm from .base import * # noqa -SECRET_KEY = libssm.get_secret("/orcabus/backend/django_secret_key") +logger = logging.getLogger(__name__) DEBUG = False -db_conn_cfg = Env.db_url_config(libssm.get_secret("/orcabus/backend/full_db_url")) +secret_id = os.environ.get("SECRET_ID", "orcabus/sequence_run_manager/rdsLoginCredential") + +try: + secret_str = libsm.get_secret(secret_id) # this will be lru cached throughout exec lifetime + sd = json.loads(secret_str, object_hook=lambda d: SimpleNamespace(**d)) + db_conn = f"{sd.engine}://{sd.username}:{sd.password}@{sd.host}:{sd.port}/{sd.dbname}" +except Exception as e: + logger.error(f"Error retrieving db secret from the Secret Manager: {e}") + raise e + +db_conn_cfg = Env.db_url_config(db_conn) DATABASES = {"default": db_conn_cfg} diff --git a/lib/workload/stateless/sequence_run_manager/sequence_run_manager_proc/lambdas/bssh_event.py b/lib/workload/stateless/sequence_run_manager/sequence_run_manager_proc/lambdas/bssh_event.py index c22de2232..dbd5063f1 100644 --- a/lib/workload/stateless/sequence_run_manager/sequence_run_manager_proc/lambdas/bssh_event.py +++ b/lib/workload/stateless/sequence_run_manager/sequence_run_manager_proc/lambdas/bssh_event.py @@ -32,6 +32,9 @@ def sqs_handler(event, context): """event payload dict + Here is how to generate an example event. See README for more. + python manage.py generate_mock_bssh_event | jq + This Lambda is to be subscribed to SQS for BSSH event through ICA v1 ENS https://illumina.gitbook.io/ica-v1/events/e-deliverytargets diff --git a/package.json b/package.json index 7f53d6f54..8d5c9010b 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,7 @@ "cdk-orcabus": "cdk --app 'yarn run -B ts-node --prefer-ts-exts bin/orcabus.ts'" }, "dependencies": { - "@aws-cdk/aws-apigatewayv2-alpha": "^2.110.0-alpha.0", - "@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.110.0-alpha.0", - "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.110.0-alpha.0", - "@aws-cdk/aws-lambda-python-alpha": "2.110.0-alpha.0", + "@aws-cdk/aws-lambda-python-alpha": "2.126.0-alpha.0", "aws-cdk-lib": "^2.126.0", "cargo-lambda-cdk": "^0.0.19", "cdk-nag": "^2.28.27", diff --git a/skel/django-api/component.ts b/skel/django-api/component.ts index cdef9652d..d9e487fdf 100644 --- a/skel/django-api/component.ts +++ b/skel/django-api/component.ts @@ -7,8 +7,8 @@ import { ISecurityGroup, IVpc } from 'aws-cdk-lib/aws-ec2'; import { IEventBus, Rule } from 'aws-cdk-lib/aws-events'; import { aws_events_targets, aws_lambda, Duration } from 'aws-cdk-lib'; import { PythonFunction, PythonLayerVersion } from '@aws-cdk/aws-lambda-python-alpha'; -import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations-alpha'; -import { CorsHttpMethod, HttpApi, HttpMethod, HttpStage } from '@aws-cdk/aws-apigatewayv2-alpha'; +import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; +import { CorsHttpMethod, HttpApi, HttpMethod, HttpStage } from 'aws-cdk-lib/aws-apigatewayv2'; export interface ProjectNameProps { // FIXME change prop interface name layers: ILayerVersion[]; diff --git a/test/stateful/stateful-deployment.test.ts b/test/stateful/stateful-deployment.test.ts index 4bf8947b8..23dce8d31 100644 --- a/test/stateful/stateful-deployment.test.ts +++ b/test/stateful/stateful-deployment.test.ts @@ -32,6 +32,10 @@ describe('cdk-nag-stateful-stack', () => { `/${stack.stackName}/OrcaBusDatabaseConstruct/OrcaBusDatabaseConstructDbSecret/Resource`, [{ id: 'AwsSolutions-SMG4', reason: 'Dont require secret rotation' }] ); + + NagSuppressions.addStackSuppressions(stack, [ + { id: 'AwsSolutions-APIG1', reason: 'See https://github.com/aws/aws-cdk/issues/11100' }, + ]); }); test('cdk-nag AwsSolutions Pack errors', () => { diff --git a/test/stateless/stateless-deployment.test.ts b/test/stateless/stateless-deployment.test.ts index e2f4591bf..3bb03111d 100644 --- a/test/stateless/stateless-deployment.test.ts +++ b/test/stateless/stateless-deployment.test.ts @@ -24,6 +24,8 @@ describe('cdk-nag-stateless-stack', () => { // stateless stack cdk-nag test Aspects.of(stack).add(new AwsSolutionsChecks()); + applyNagSuppression(stack.node.id, stack); + test(`OrcaBusStatelessStack: cdk-nag AwsSolutions Pack errors`, () => { const errors = Annotations.fromStack(stack) .findError('*', Match.stringLikeRegexp('AwsSolutions-.*')) @@ -68,12 +70,39 @@ describe('cdk-nag-stateless-stack', () => { * @param stack */ function applyNagSuppression(stackId: string, stack: Stack) { + // all stacks widely + NagSuppressions.addStackSuppressions( + stack, + [{ id: 'AwsSolutions-IAM4', reason: 'allow to use AWS managed policy' }], + true + ); + + NagSuppressions.addStackSuppressions( + stack, + [ + { + id: 'AwsSolutions-APIG1', + reason: 'See https://github.com/aws/aws-cdk/issues/11100', + }, + ], + true + ); + + NagSuppressions.addStackSuppressions( + stack, + [ + { + id: 'AwsSolutions-APIG4', + reason: 'We have the default Cognito UserPool authorizer', + }, + ], + true + ); + + // for each stack specific + switch (stackId) { case 'PostgresManager': - NagSuppressions.addStackSuppressions(stack, [ - { id: 'AwsSolutions-IAM4', reason: 'allow to use AWS managed policy' }, - ]); - // suppress by resource NagSuppressions.addResourceSuppressionsByPath( stack, diff --git a/yarn.lock b/yarn.lock index 53870ff35..a4df3e406 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,45 +43,13 @@ __metadata: languageName: node linkType: hard -"@aws-cdk/aws-apigatewayv2-alpha@npm:^2.110.0-alpha.0": - version: 2.110.0-alpha.0 - resolution: "@aws-cdk/aws-apigatewayv2-alpha@npm:2.110.0-alpha.0" +"@aws-cdk/aws-lambda-python-alpha@npm:2.126.0-alpha.0": + version: 2.126.0-alpha.0 + resolution: "@aws-cdk/aws-lambda-python-alpha@npm:2.126.0-alpha.0" peerDependencies: - aws-cdk-lib: ^2.110.0 - constructs: ^10.0.0 - checksum: e850053a39af974268a96d6cc6cfd4b5d3ac7409d30dee5a5072c61474603ae131d3cd5279e1112fee4543c7ce47b8c0a35398b2e993a242ce2b167657ad1fcf - languageName: node - linkType: hard - -"@aws-cdk/aws-apigatewayv2-authorizers-alpha@npm:^2.110.0-alpha.0": - version: 2.110.0-alpha.0 - resolution: "@aws-cdk/aws-apigatewayv2-authorizers-alpha@npm:2.110.0-alpha.0" - peerDependencies: - "@aws-cdk/aws-apigatewayv2-alpha": 2.110.0-alpha.0 - aws-cdk-lib: ^2.110.0 - constructs: ^10.0.0 - checksum: f6891878d3eb8e5b5ebadead61a28c936c89fb150bc2c766f900a56e7cb8908dc23177c8e1ff247817e2a4ce48cdc0ccf1f89c3db5132a519f9d39b6c3a7962e - languageName: node - linkType: hard - -"@aws-cdk/aws-apigatewayv2-integrations-alpha@npm:^2.110.0-alpha.0": - version: 2.110.0-alpha.0 - resolution: "@aws-cdk/aws-apigatewayv2-integrations-alpha@npm:2.110.0-alpha.0" - peerDependencies: - "@aws-cdk/aws-apigatewayv2-alpha": 2.110.0-alpha.0 - aws-cdk-lib: ^2.110.0 - constructs: ^10.0.0 - checksum: e2bca14e9154d8706398324e805d8912a5ad4fea589130e242f62d00675ade12627c5d74ddca231e039e1954bef698a04fe2aec4766cf6d123e0fb5108790e4c - languageName: node - linkType: hard - -"@aws-cdk/aws-lambda-python-alpha@npm:2.110.0-alpha.0": - version: 2.110.0-alpha.0 - resolution: "@aws-cdk/aws-lambda-python-alpha@npm:2.110.0-alpha.0" - peerDependencies: - aws-cdk-lib: ^2.110.0 + aws-cdk-lib: ^2.126.0 constructs: ^10.0.0 - checksum: 2f52d4ba1efbb37e545bb31a623f5fe582b009e3f599d666d23181158e83b00d3a16147b368a905eb2edcc3731aea5efcf2808483ffb8e8592ec6cca954063e9 + checksum: b7e18df1c66a0c17be8c873a58cd10405269cb0b80f7d8e6913fbf853c1f52f46a8290301ed0b8e79264503f08820d7470fc4378fcb81eaf7532483d92aa3de8 languageName: node linkType: hard @@ -1477,8 +1445,8 @@ __metadata: linkType: hard "aws-cdk-lib@npm:^2.126.0": - version: 2.126.0 - resolution: "aws-cdk-lib@npm:2.126.0" + version: 2.132.1 + resolution: "aws-cdk-lib@npm:2.132.1" dependencies: "@aws-cdk/asset-awscli-v1": ^2.2.202 "@aws-cdk/asset-kubectl-v20": ^2.1.2 @@ -1486,16 +1454,17 @@ __metadata: "@balena/dockerignore": ^1.0.2 case: 1.6.3 fs-extra: ^11.2.0 - ignore: ^5.3.0 + ignore: ^5.3.1 jsonschema: ^1.4.1 + mime-types: ^2.1.35 minimatch: ^3.1.2 punycode: ^2.3.1 - semver: ^7.5.4 + semver: ^7.6.0 table: ^6.8.1 yaml: 1.10.2 peerDependencies: constructs: ^10.0.0 - checksum: 62d46d108fba4ef2c76e7c46767bddfe7019b2f96ad1bfb1b8ae348eeefb889dc6314b56fa5da16c299477ebc1463354de93df45a0ca72cc4d6faee5a0eb9656 + checksum: eb017fff6aa72b2d0cebaf317971b4b9f803322feb3fbc9b940f5dd72fe65213f92880a8b0fe6214a860b6ad985df2ad6839e4ed15d899dbdff8c5da1728230f languageName: node linkType: hard @@ -2681,13 +2650,20 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.0": +"ignore@npm:^5.2.0, ignore@npm:^5.2.4": version: 5.3.0 resolution: "ignore@npm:5.3.0" checksum: 2736da6621f14ced652785cb05d86301a66d70248597537176612bd0c8630893564bd5f6421f8806b09e8472e75c591ef01672ab8059c07c6eb2c09cefe04bf9 languageName: node linkType: hard +"ignore@npm:^5.3.1": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3 + languageName: node + linkType: hard + "import-fresh@npm:^3.2.1": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -3657,6 +3633,22 @@ __metadata: languageName: node linkType: hard +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f + languageName: node + linkType: hard + +"mime-types@npm:^2.1.35": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -3893,10 +3885,7 @@ __metadata: version: 0.0.0-use.local resolution: "orcabus@workspace:." dependencies: - "@aws-cdk/aws-apigatewayv2-alpha": ^2.110.0-alpha.0 - "@aws-cdk/aws-apigatewayv2-authorizers-alpha": ^2.110.0-alpha.0 - "@aws-cdk/aws-apigatewayv2-integrations-alpha": ^2.110.0-alpha.0 - "@aws-cdk/aws-lambda-python-alpha": 2.110.0-alpha.0 + "@aws-cdk/aws-lambda-python-alpha": 2.126.0-alpha.0 "@types/jest": ^29.5.2 "@types/node": ^20.4.0 "@typescript-eslint/eslint-plugin": ^6.19.1