From 9e9cc277d55774b0b2e8887676ddb328eb662d74 Mon Sep 17 00:00:00 2001 From: mazyu36 Date: Tue, 25 Jun 2024 03:49:49 +0900 Subject: [PATCH] feat(apprunner): add ObservabilityConfiguration for AppRunner Service (#30359) ### Issue # (if applicable) Closes #22985 . ### Reason for this change At the moment, L2 Construct does not support a tracing setting for the AppRunner Service. ### Description of changes * Add `ObservabilityConfiguration` Class * Add `observabilityConfiguration` property to the `Service` Class ### Description of how you validated changes Add unit tests and integ tests. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-apprunner-alpha/README.md | 19 ++ .../@aws-cdk/aws-apprunner-alpha/lib/index.ts | 1 + .../lib/observability-configuration.ts | 163 ++++++++++++ .../aws-apprunner-alpha/lib/service.ts | 13 + ...efaultTestDeployAssertFEB7E279.assets.json | 19 ++ ...aultTestDeployAssertFEB7E279.template.json | 36 +++ .../cdk.out | 1 + ...er-observability-configuration.assets.json | 19 ++ ...-observability-configuration.template.json | 120 +++++++++ .../integ.json | 12 + .../manifest.json | 131 ++++++++++ .../tree.json | 235 ++++++++++++++++++ ...teg.service-observability-configuration.ts | 31 +++ .../test/obserbability-configuration.test.ts | 61 +++++ .../aws-apprunner-alpha/test/service.test.ts | 53 ++++ 15 files changed, 914 insertions(+) create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/lib/observability-configuration.ts create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.template.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.assets.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.template.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.ts create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/obserbability-configuration.test.ts diff --git a/packages/@aws-cdk/aws-apprunner-alpha/README.md b/packages/@aws-cdk/aws-apprunner-alpha/README.md index 4f282c2f50396..36d30571c08d9 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/README.md +++ b/packages/@aws-cdk/aws-apprunner-alpha/README.md @@ -296,3 +296,22 @@ new apprunner.Service(this, 'Service', { }), }); ``` + +## Observability Configuration + +To associate an App Runner service with a custom observability configuration, use the `observabilityConfiguration` property. + +```ts +const observabilityConfiguration = new apprunner.ObservabilityConfiguration(this, 'ObservabilityConfiguration', { + observabilityConfigurationName: 'MyObservabilityConfiguration', + traceConfigurationVendor: apprunner.TraceConfigurationVendor.AWSXRAY, +}); + +new apprunner.Service(this, 'DemoService', { + source: apprunner.Source.fromEcrPublic({ + imageConfiguration: { port: 8000 }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + observabilityConfiguration, +}); +``` diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts index 1908a112b1ae7..13389d0e10c14 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts @@ -1,4 +1,5 @@ // AWS::AppRunner CloudFormation Resources: export * from './auto-scaling-configuration'; +export * from './observability-configuration'; export * from './service'; export * from './vpc-connector'; diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/observability-configuration.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/observability-configuration.ts new file mode 100644 index 0000000000000..56be971ec5500 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/observability-configuration.ts @@ -0,0 +1,163 @@ +import * as cdk from 'aws-cdk-lib/core'; +import { Construct } from 'constructs'; +import { CfnObservabilityConfiguration } from 'aws-cdk-lib/aws-apprunner'; + +/** + * The implementation provider chosen for tracing App Runner services + * + * @see https://docs.aws.amazon.com/apprunner/latest/dg/monitor.html + */ +export enum TraceConfigurationVendor { + /** + * Tracing (X-Ray) + */ + AWSXRAY = 'AWSXRAY', +} + +/** + * Properties of the AppRunner Observability configuration + */ +export interface ObservabilityConfigurationProps { + /** + * The name for the ObservabilityConfiguration. + * + * @default - a name generated by CloudFormation + */ + readonly observabilityConfigurationName?: string; + + /** + * The implementation provider chosen for tracing App Runner services. + */ + readonly traceConfigurationVendor: TraceConfigurationVendor; +} + +/** + * Attributes for the App Runner Observability configuration + */ +export interface ObservabilityConfigurationAttributes { + /** + * The name of the Observability configuration. + */ + readonly observabilityConfigurationName: string; + + /** + * The revision of the Observability configuration. + */ + readonly observabilityConfigurationRevision: number; +} + +/** + * Represents the App Runner Observability configuration. + */ +export interface IObservabilityConfiguration extends cdk.IResource { + /** + * The Name of the Observability configuration. + * @attribute + */ + readonly observabilityConfigurationName: string; + + /** + * The ARN of the Observability configuration. + * @attribute + */ + readonly observabilityConfigurationArn: string; + + /** + * The revision of the Observability configuration. + * @attribute + */ + readonly observabilityConfigurationRevision: number; +} + +/** + * The App Runner Observability configuration + * + * @resource AWS::AppRunner::ObservabilityConfiguration + */ +export class ObservabilityConfiguration extends cdk.Resource implements IObservabilityConfiguration { + /** + * Imports an App Runner Observability Configuration from attributes. + */ + public static fromObservabilityConfigurationAttributes(scope: Construct, id: string, + attrs: ObservabilityConfigurationAttributes): IObservabilityConfiguration { + const observabilityConfigurationName = attrs.observabilityConfigurationName; + const observabilityConfigurationRevision = attrs.observabilityConfigurationRevision; + + class Import extends cdk.Resource implements IObservabilityConfiguration { + public readonly observabilityConfigurationName = observabilityConfigurationName; + public readonly observabilityConfigurationRevision = observabilityConfigurationRevision; + public readonly observabilityConfigurationArn = cdk.Stack.of(this).formatArn({ + resource: 'observabilityconfiguration', + service: 'apprunner', + resourceName: `${attrs.observabilityConfigurationName}/${attrs.observabilityConfigurationRevision}`, + }); + } + + return new Import(scope, id); + } + + /** + * Imports an App Runner Observability Configuration from its ARN + */ + public static fromArn(scope: Construct, id: string, observabilityConfigurationArn: string): IObservabilityConfiguration { + const resourceParts = cdk.Fn.split('/', observabilityConfigurationArn); + + if (!resourceParts || resourceParts.length < 3) { + throw new Error(`Unexpected ARN format: ${observabilityConfigurationArn}`); + } + + const observabilityConfigurationName = cdk.Fn.select(0, resourceParts); + const observabilityConfigurationRevision = Number(cdk.Fn.select(1, resourceParts)); + + class Import extends cdk.Resource implements IObservabilityConfiguration { + public readonly observabilityConfigurationName = observabilityConfigurationName; + public readonly observabilityConfigurationRevision = observabilityConfigurationRevision; + public readonly observabilityConfigurationArn = observabilityConfigurationArn; + } + + return new Import(scope, id); + } + + /** + * The ARN of the Observability configuration. + * @attribute + */ + readonly observabilityConfigurationArn: string; + + /** + * The revision of the Observability configuration. + * @attribute + */ + readonly observabilityConfigurationRevision: number; + + /** + * The name of the Observability configuration. + * @attribute + */ + readonly observabilityConfigurationName: string; + + public constructor(scope: Construct, id: string, props: ObservabilityConfigurationProps) { + super(scope, id, { + physicalName: props.observabilityConfigurationName, + }); + + if ( + props.observabilityConfigurationName !== undefined && + !cdk.Token.isUnresolved(props.observabilityConfigurationName) && + !/^[A-Za-z0-9][A-Za-z0-9\-_]{3,31}$/.test(props.observabilityConfigurationName) + ) { + throw new Error(`observabilityConfigurationName must match the \`^[A-Za-z0-9][A-Za-z0-9\-_]{3,31}$\` pattern, got ${props.observabilityConfigurationName}`); + } + + const resource = new CfnObservabilityConfiguration(this, 'Resource', { + observabilityConfigurationName: props.observabilityConfigurationName, + traceConfiguration: { + vendor: props.traceConfigurationVendor, + }, + }); + + this.observabilityConfigurationArn = resource.attrObservabilityConfigurationArn; + this.observabilityConfigurationRevision = resource.attrObservabilityConfigurationRevision; + this.observabilityConfigurationName = resource.ref; + } +} diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts index 58babba734037..a93e8bcff5602 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts @@ -10,6 +10,7 @@ import { Construct } from 'constructs'; import { CfnService } from 'aws-cdk-lib/aws-apprunner'; import { IVpcConnector } from './vpc-connector'; import { IAutoScalingConfiguration } from './auto-scaling-configuration'; +import { IObservabilityConfiguration } from './observability-configuration'; /** * The image repository types @@ -743,6 +744,14 @@ export interface ServiceProps { * @default - IpAddressType.IPV4 */ readonly ipAddressType?: IpAddressType; + + /** + * Settings for an App Runner observability configuration. + * + * @default - no observability configuration resource is associated with the service. + */ + readonly observabilityConfiguration?: IObservabilityConfiguration; + } /** @@ -1296,6 +1305,10 @@ export class Service extends cdk.Resource implements iam.IGrantable { healthCheckConfiguration: this.props.healthCheck ? this.props.healthCheck.bind() : undefined, + observabilityConfiguration: props.observabilityConfiguration ? { + observabilityEnabled: true, + observabilityConfigurationArn: props.observabilityConfiguration.observabilityConfigurationArn, + } : undefined, }); // grant required privileges for the role diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets.json new file mode 100644 index 0000000000000..e9f8f8a38b121 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/cdk.out b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.assets.json new file mode 100644 index 0000000000000..eac050d6296f7 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "839ca74bb662f9fc4cef96d00bbc37a8739def22f31ef235eb3dc66132b90e9c": { + "source": { + "path": "integ-apprunner-observability-configuration.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "839ca74bb662f9fc4cef96d00bbc37a8739def22f31ef235eb3dc66132b90e9c.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.template.json new file mode 100644 index 0000000000000..e98b564b36943 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ-apprunner-observability-configuration.template.json @@ -0,0 +1,120 @@ +{ + "Resources": { + "ObservabilityConfiguration68CE4C7A": { + "Type": "AWS::AppRunner::ObservabilityConfiguration", + "Properties": { + "TraceConfiguration": { + "Vendor": "AWSXRAY" + } + } + }, + "ServiceInstanceRoleDFA90CEC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ServiceDBC79909": { + "Type": "AWS::AppRunner::Service", + "Properties": { + "InstanceConfiguration": { + "InstanceRoleArn": { + "Fn::GetAtt": [ + "ServiceInstanceRoleDFA90CEC", + "Arn" + ] + } + }, + "NetworkConfiguration": { + "EgressConfiguration": { + "EgressType": "DEFAULT" + } + }, + "ObservabilityConfiguration": { + "ObservabilityConfigurationArn": { + "Fn::GetAtt": [ + "ObservabilityConfiguration68CE4C7A", + "ObservabilityConfigurationArn" + ] + }, + "ObservabilityEnabled": true + }, + "ServiceName": "service", + "SourceConfiguration": { + "AuthenticationConfiguration": {}, + "AutoDeploymentsEnabled": false, + "ImageRepository": { + "ImageConfiguration": { + "Port": "8000" + }, + "ImageIdentifier": "public.ecr.aws/aws-containers/hello-app-runner:latest", + "ImageRepositoryType": "ECR_PUBLIC" + } + } + } + } + }, + "Outputs": { + "URL": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "ServiceDBC79909", + "ServiceUrl" + ] + } + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ.json new file mode 100644 index 0000000000000..bdeee25d506b6 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "AppRunnerObservabilityConfiguration/DefaultTest": { + "stacks": [ + "integ-apprunner-observability-configuration" + ], + "assertionStack": "AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert", + "assertionStackName": "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/manifest.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/manifest.json new file mode 100644 index 0000000000000..913b385e121a3 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-apprunner-observability-configuration.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-apprunner-observability-configuration.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-apprunner-observability-configuration": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-apprunner-observability-configuration.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/839ca74bb662f9fc4cef96d00bbc37a8739def22f31ef235eb3dc66132b90e9c.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-apprunner-observability-configuration.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-apprunner-observability-configuration.assets" + ], + "metadata": { + "/integ-apprunner-observability-configuration/ObservabilityConfiguration/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ObservabilityConfiguration68CE4C7A" + } + ], + "/integ-apprunner-observability-configuration/Service/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServiceInstanceRoleDFA90CEC" + } + ], + "/integ-apprunner-observability-configuration/Service/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServiceDBC79909" + } + ], + "/integ-apprunner-observability-configuration/URL": [ + { + "type": "aws:cdk:logicalId", + "data": "URL" + } + ], + "/integ-apprunner-observability-configuration/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-apprunner-observability-configuration/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-apprunner-observability-configuration" + }, + "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "AppRunnerObservabilityConfigurationDefaultTestDeployAssertFEB7E279.assets" + ], + "metadata": { + "/AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/tree.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/tree.json new file mode 100644 index 0000000000000..af1a3f2a658e9 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.js.snapshot/tree.json @@ -0,0 +1,235 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-apprunner-observability-configuration": { + "id": "integ-apprunner-observability-configuration", + "path": "integ-apprunner-observability-configuration", + "children": { + "ObservabilityConfiguration": { + "id": "ObservabilityConfiguration", + "path": "integ-apprunner-observability-configuration/ObservabilityConfiguration", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-apprunner-observability-configuration/ObservabilityConfiguration/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppRunner::ObservabilityConfiguration", + "aws:cdk:cloudformation:props": { + "traceConfiguration": { + "vendor": "AWSXRAY" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apprunner.CfnObservabilityConfiguration", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Service": { + "id": "Service", + "path": "integ-apprunner-observability-configuration/Service", + "children": { + "InstanceRole": { + "id": "InstanceRole", + "path": "integ-apprunner-observability-configuration/Service/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "integ-apprunner-observability-configuration/Service/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner-observability-configuration/Service/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner-observability-configuration/Service/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppRunner::Service", + "aws:cdk:cloudformation:props": { + "instanceConfiguration": { + "instanceRoleArn": { + "Fn::GetAtt": [ + "ServiceInstanceRoleDFA90CEC", + "Arn" + ] + } + }, + "networkConfiguration": { + "egressConfiguration": { + "egressType": "DEFAULT" + } + }, + "observabilityConfiguration": { + "observabilityEnabled": true, + "observabilityConfigurationArn": { + "Fn::GetAtt": [ + "ObservabilityConfiguration68CE4C7A", + "ObservabilityConfigurationArn" + ] + } + }, + "serviceName": "service", + "sourceConfiguration": { + "authenticationConfiguration": {}, + "autoDeploymentsEnabled": false, + "imageRepository": { + "imageConfiguration": { + "port": "8000" + }, + "imageIdentifier": "public.ecr.aws/aws-containers/hello-app-runner:latest", + "imageRepositoryType": "ECR_PUBLIC" + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apprunner.CfnService", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "URL": { + "id": "URL", + "path": "integ-apprunner-observability-configuration/URL", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-apprunner-observability-configuration/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-apprunner-observability-configuration/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "AppRunnerObservabilityConfiguration": { + "id": "AppRunnerObservabilityConfiguration", + "path": "AppRunnerObservabilityConfiguration", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "AppRunnerObservabilityConfiguration/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "AppRunnerObservabilityConfiguration/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "AppRunnerObservabilityConfiguration/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.ts new file mode 100644 index 0000000000000..8d3ec742b04b8 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-observability-configuration.ts @@ -0,0 +1,31 @@ +import * as cdk from 'aws-cdk-lib'; +import { Service, Source, ObservabilityConfiguration, TraceConfigurationVendor } from '../lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'integ-apprunner-observability-configuration'); + +const observabilityConfiguration = new ObservabilityConfiguration(stack, 'ObservabilityConfiguration', { + traceConfigurationVendor: TraceConfigurationVendor.AWSXRAY, +}); + +const service = new Service(stack, 'Service', { + serviceName: 'service', + source: Source.fromEcrPublic({ + imageConfiguration: { + port: 8000, + }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + autoDeploymentsEnabled: false, + observabilityConfiguration, +}); + +new cdk.CfnOutput(stack, 'URL', { value: `https://${service.serviceUrl}` }); + +new integ.IntegTest(app, 'AppRunnerObservabilityConfiguration', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/obserbability-configuration.test.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/obserbability-configuration.test.ts new file mode 100644 index 0000000000000..d450c9a04f976 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/obserbability-configuration.test.ts @@ -0,0 +1,61 @@ +import { Template } from 'aws-cdk-lib/assertions'; +import * as cdk from 'aws-cdk-lib'; +import { ObservabilityConfiguration, TraceConfigurationVendor } from '../lib'; + +let stack: cdk.Stack; +beforeEach(() => { + stack = new cdk.Stack(); +}); + +test.each([ + ['MyObservabilityConfiguration'], + ['my-observability-configuration_1'], +])('create a ObservabilityConfiguration with all properties (name: %s)', (observabilityConfigurationName: string) => { + // WHEN + new ObservabilityConfiguration(stack, 'ObservabilityConfiguration', { + observabilityConfigurationName, + traceConfigurationVendor: TraceConfigurationVendor.AWSXRAY, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::ObservabilityConfiguration', { + ObservabilityConfigurationName: observabilityConfigurationName, + TraceConfiguration: { + Vendor: 'AWSXRAY', + }, + }); +}); + +test.each([ + ['tes'], + ['test-observability-configuration-name-over-limitation'], + ['-test'], + ['test-?'], +])('observabilityConfigurationName over length limitation (name: %s)', (observabilityConfigurationName: string) => { + expect(() => { + new ObservabilityConfiguration(stack, 'ObservabilityConfiguration', { + observabilityConfigurationName, + traceConfigurationVendor: TraceConfigurationVendor.AWSXRAY, + }); + }).toThrow(`observabilityConfigurationName must match the \`^[A-Za-z0-9][A-Za-z0-9\-_]{3,31}$\` pattern, got ${observabilityConfigurationName}`); +}); + +test('create an Auto scaling Configuration with tags', () => { + // WHEN + const observabilityConfiguration = new ObservabilityConfiguration(stack, 'ObservabilityConfiguration', { + observabilityConfigurationName: 'my-autoscaling-config', + traceConfigurationVendor: TraceConfigurationVendor.AWSXRAY, + }); + + cdk.Tags.of(observabilityConfiguration).add('Environment', 'production'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::ObservabilityConfiguration', { + Tags: [ + { + Key: 'Environment', + Value: 'production', + }, + ], + }); +}); diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts index 8a27342b2c987..56eb9d9218ed3 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts @@ -1654,3 +1654,56 @@ test('create a service with an AutoScalingConfiguration', () => { AutoScalingConfigurationArn: stack.resolve(autoScalingConfiguration.autoScalingConfigurationArn), }); }); + +test('create a service with a Observability Configuration', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'demo-stack'); + const observabilityConfiguration = new apprunner.ObservabilityConfiguration(stack, 'ObservabilityConfiguration', { + observabilityConfigurationName: 'MyObservabilityConfiguration', + traceConfigurationVendor: apprunner.TraceConfigurationVendor.AWSXRAY, + }); + + // WHEN + new apprunner.Service(stack, 'DemoService', { + source: apprunner.Source.fromEcrPublic({ + imageConfiguration: { port: 8000 }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + observabilityConfiguration, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::ObservabilityConfiguration', { + ObservabilityConfigurationName: 'MyObservabilityConfiguration', + TraceConfiguration: { + Vendor: 'AWSXRAY', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', { + ObservabilityConfiguration: { + ObservabilityEnabled: true, + ObservabilityConfigurationArn: stack.resolve(observabilityConfiguration.observabilityConfigurationArn), + }, + }); +}); + +test('create a service without a Observability Configuration', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'demo-stack'); + + // WHEN + new apprunner.Service(stack, 'DemoService', { + source: apprunner.Source.fromEcrPublic({ + imageConfiguration: { port: 8000 }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', { + ObservabilityConfiguration: Match.absent(), + }); +});