From 8c8a4f990008ec9fdecf74bd79f27757b401dcb7 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Tue, 18 Oct 2022 15:57:10 -0700 Subject: [PATCH 1/7] feat(sagemaker): add EndpointConfig L2 construct This is the second of three PRs to complete the implementation of RFC 431: https://github.com/aws/aws-cdk-rfcs/issues/431 related to #2809 Co-authored-by: Matt McClean Co-authored-by: Long Yao Co-authored-by: Drew Jetter <60628154+jetterdj@users.noreply.github.com> Co-authored-by: Murali Ganesh <59461079+foxpro24@users.noreply.github.com> Co-authored-by: Abilash Rangoju <988529+rangoju@users.noreply.github.com> --- packages/@aws-cdk/aws-sagemaker/README.md | 39 + .../aws-sagemaker/lib/accelerator-type.ts | 64 + .../aws-sagemaker/lib/endpoint-config.ts | 313 ++++ packages/@aws-cdk/aws-sagemaker/lib/index.ts | 3 + .../aws-sagemaker/lib/instance-type.ts | 453 ++++++ .../aws-sagemaker/lib/private/util.ts | 13 + packages/@aws-cdk/aws-sagemaker/package.json | 2 + .../test/endpoint-config.test.ts | 359 +++++ ...35ad29d4291eb86675a64c75fa4f1338916.tar.gz | Bin 0 -> 385 bytes .../Dockerfile | 19 + .../index.html | 1 + .../index.py | 48 + ...s-cdk-sagemaker-endpointconfig.assets.json | 45 + ...cdk-sagemaker-endpointconfig.template.json | 756 ++++++++++ .../integ.endpoint-config.js.snapshot/cdk.out | 1 + .../integ.json | 12 + ...efaultTestDeployAssert8D52A281.assets.json | 19 + ...aultTestDeployAssert8D52A281.template.json | 36 + .../manifest.json | 291 ++++ .../tree.json | 1259 +++++++++++++++++ .../test/integ.endpoint-config.ts | 78 + 21 files changed, 3811 insertions(+) create mode 100644 packages/@aws-cdk/aws-sagemaker/lib/accelerator-type.ts create mode 100644 packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts create mode 100644 packages/@aws-cdk/aws-sagemaker/lib/instance-type.ts create mode 100644 packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.tar.gz create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/Dockerfile create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.html create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.py create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.assets.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.template.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.assets.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.template.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.ts diff --git a/packages/@aws-cdk/aws-sagemaker/README.md b/packages/@aws-cdk/aws-sagemaker/README.md index eeb1bc7e9eede..1fb468e4e760b 100644 --- a/packages/@aws-cdk/aws-sagemaker/README.md +++ b/packages/@aws-cdk/aws-sagemaker/README.md @@ -156,3 +156,42 @@ import * as sagemaker from '@aws-cdk/aws-sagemaker'; const bucket = new s3.Bucket(this, 'MyBucket'); const modelData = sagemaker.ModelData.fromBucket(bucket, 'path/to/artifact/file.tar.gz'); ``` + +## Model Hosting + +Amazon SageMaker provides model hosting services for model deployment. Amazon SageMaker provides an +HTTPS endpoint where your machine learning model is available to provide inferences. + +### Endpoint Configuration + +By using the `EndpointConfig` construct, you can define a set of endpoint configuration which can be +used to provision one or more endpoints. In this configuration, you identify one or more models to +deploy and the resources that you want Amazon SageMaker to provision. You define one or more +production variants, each of which identifies a model. Each production variant also describes the +resources that you want Amazon SageMaker to provision. If you are hosting multiple models, you also +assign a variant weight to specify how much traffic you want to allocate to each model. For example, +suppose that you want to host two models, A and B, and you assign traffic weight 2 for model A and 1 +for model B. Amazon SageMaker distributes two-thirds of the traffic to Model A, and one-third to +model B: + +```typescript +import * as sagemaker from '@aws-cdk/aws-sagemaker'; + +declare const modelA: sagemaker.Model; +declare const modelB: sagemaker.Model; + +const endpointConfig = new sagemaker.EndpointConfig(this, 'EndpointConfig', { + instanceProductionVariants: [ + { + model: modelA, + variantName: 'modelA', + initialVariantWeight: 2.0, + }, + { + model: modelB, + variantName: 'variantB', + initialVariantWeight: 1.0, + }, + ] +}); +``` diff --git a/packages/@aws-cdk/aws-sagemaker/lib/accelerator-type.ts b/packages/@aws-cdk/aws-sagemaker/lib/accelerator-type.ts new file mode 100644 index 0000000000000..cec364b4e9c1d --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/lib/accelerator-type.ts @@ -0,0 +1,64 @@ +import * as cdk from '@aws-cdk/core'; + +/** + * Supported Elastic Inference (EI) instance types for SageMaker instance-based production variants. + * EI instances provide on-demand GPU computing for inference. + */ +export class AcceleratorType { + /** + * ml.eia1.large + */ + public static readonly EIA1_LARGE = AcceleratorType.of('ml.eia1.large'); + + /** + * ml.eia1.medium + */ + public static readonly EIA1_MEDIUM = AcceleratorType.of('ml.eia1.medium'); + + /** + * ml.eia1.xlarge + */ + public static readonly EIA1_XLARGE = AcceleratorType.of('ml.eia1.xlarge'); + + /** + * ml.eia2.large + */ + public static readonly EIA2_LARGE = AcceleratorType.of('ml.eia2.large'); + + /** + * ml.eia2.medium + */ + public static readonly EIA2_MEDIUM = AcceleratorType.of('ml.eia2.medium'); + + /** + * ml.eia2.xlarge + */ + public static readonly EIA2_XLARGE = AcceleratorType.of('ml.eia2.xlarge'); + + /** + * Builds an AcceleratorType from a given string or token (such as a CfnParameter). + * @param acceleratorType An accelerator type as string + * @returns A strongly typed AcceleratorType + */ + public static of(acceleratorType: string): AcceleratorType { + return new AcceleratorType(acceleratorType); + } + + private readonly acceleratorTypeIdentifier: string; + + constructor(acceleratorType: string) { + if (cdk.Token.isUnresolved(acceleratorType) || acceleratorType.startsWith('ml.')) { + this.acceleratorTypeIdentifier = acceleratorType; + } else { + throw new Error(`instance type must start with 'ml.'; (got ${acceleratorType})`); + } + } + + /** + * Return the accelerator type as a string + * @returns The accelerator type as a string + */ + public toString(): string { + return this.acceleratorTypeIdentifier; + } +} diff --git a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts new file mode 100644 index 0000000000000..644e9c35580fe --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts @@ -0,0 +1,313 @@ +import { EOL } from 'os'; +import * as kms from '@aws-cdk/aws-kms'; +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { AcceleratorType } from './accelerator-type'; +import { InstanceType } from './instance-type'; +import { IModel } from './model'; +import { sameEnvDimension } from './private/util'; +import { CfnEndpointConfig } from './sagemaker.generated'; + +/** + * The interface for a SageMaker EndpointConfig resource. + */ +export interface IEndpointConfig extends cdk.IResource { + /** + * The ARN of the endpoint configuration. + * + * @attribute + */ + readonly endpointConfigArn: string; + /** + * The name of the endpoint configuration. + * + * @attribute + */ + readonly endpointConfigName: string; +} + +/** + * Common construction properties for all production variant types (e.g., instance, serverless). + */ +interface ProductionVariantProps { + /** + * Determines initial traffic distribution among all of the models that you specify in the + * endpoint configuration. The traffic to a production variant is determined by the ratio of the + * variant weight to the sum of all variant weight values across all production variants. + * + * @default 1.0 + */ + readonly initialVariantWeight?: number; + /** + * The model to host. + */ + readonly model: IModel; + /** + * Name of the production variant. + */ + readonly variantName: string; +} + +/** + * Construction properties for an instance production variant. + */ +export interface InstanceProductionVariantProps extends ProductionVariantProps { + /** + * The size of the Elastic Inference (EI) instance to use for the production variant. EI instances + * provide on-demand GPU computing for inference. + * + * @default - none + */ + readonly acceleratorType?: AcceleratorType; + /** + * Number of instances to launch initially. + * + * @default 1 + */ + readonly initialInstanceCount?: number; + /** + * Instance type of the production variant. + * + * @default - ml.t2.medium instance type. + */ + readonly instanceType?: InstanceType; +} + +/** + * Represents common attributes of all production variant types (e.g., instance, serverless) once + * associated to an EndpointConfig. + */ +interface ProductionVariant { + /** + * Determines initial traffic distribution among all of the models that you specify in the + * endpoint configuration. The traffic to a production variant is determined by the ratio of the + * variant weight to the sum of all variant weight values across all production variants. + */ + readonly initialVariantWeight: number; + /** + * The name of the model to host. + */ + readonly modelName: string; + /** + * The name of the production variant. + */ + readonly variantName: string; +} + +/** + * Represents an instance production variant that has been associated with an EndpointConfig. + */ +export interface InstanceProductionVariant extends ProductionVariant { + /** + * The size of the Elastic Inference (EI) instance to use for the production variant. EI instances + * provide on-demand GPU computing for inference. + * + * @default - none + */ + readonly acceleratorType?: AcceleratorType; + /** + * Number of instances to launch initially. + */ + readonly initialInstanceCount: number; + /** + * Instance type of the production variant. + */ + readonly instanceType: InstanceType; +} + +/** + * Construction properties for a SageMaker EndpointConfig. + */ +export interface EndpointConfigProps { + /** + * Name of the endpoint configuration. + * + * @default - AWS CloudFormation generates a unique physical ID and uses that ID for the endpoint + * configuration's name. + */ + readonly endpointConfigName?: string; + + /** + * Optional KMS encryption key associated with this stream. + * + * @default - none + */ + readonly encryptionKey?: kms.IKey; + + /** + * A list of instance production variants. You can always add more variants later by calling + * {@link EndpointConfig#addInstanceProductionVariant}. + * + * @default - none + */ + readonly instanceProductionVariants?: InstanceProductionVariantProps[]; +} + +/** + * Defines a SageMaker EndpointConfig. + */ +export class EndpointConfig extends cdk.Resource implements IEndpointConfig { + /** + * Imports an EndpointConfig defined either outside the CDK or in a different CDK stack. + * @param scope the Construct scope. + * @param id the resource id. + * @param endpointConfigArn the ARN of the endpoint configuration. + */ + public static fromEndpointConfigArn(scope: Construct, id: string, endpointConfigArn: string): IEndpointConfig { + const endpointConfigName = cdk.Stack.of(scope).splitArn(endpointConfigArn, cdk.ArnFormat.SLASH_RESOURCE_NAME).resourceName!; + + class Import extends cdk.Resource implements IEndpointConfig { + public endpointConfigArn = endpointConfigArn; + public endpointConfigName = endpointConfigName; + } + + return new Import(scope, id, { + environmentFromArn: endpointConfigArn, + }); + } + + /** + * Imports an EndpointConfig defined either outside the CDK or in a different CDK stack. + * @param scope the Construct scope. + * @param id the resource id. + * @param endpointConfigName the name of the endpoint configuration. + */ + public static fromEndpointConfigName(scope: Construct, id: string, endpointConfigName: string): IEndpointConfig { + const endpointConfigArn = cdk.Stack.of(scope).formatArn({ + service: 'sagemaker', + resource: 'endpoint-config', + resourceName: endpointConfigName, + }); + + class Import extends cdk.Resource implements IEndpointConfig { + public endpointConfigArn = endpointConfigArn; + public endpointConfigName = endpointConfigName; + } + + return new Import(scope, id, { + environmentFromArn: endpointConfigArn, + }); + } + + /** + * The ARN of the endpoint configuration. + */ + public readonly endpointConfigArn: string; + /** + * The name of the endpoint configuration. + */ + public readonly endpointConfigName: string; + + private readonly _instanceProductionVariants: { [key: string]: InstanceProductionVariant; } = {}; + + constructor(scope: Construct, id: string, props: EndpointConfigProps = {}) { + super(scope, id, { + physicalName: props.endpointConfigName, + }); + + (props.instanceProductionVariants || []).map(p => this.addInstanceProductionVariant(p)); + + // create the endpoint configuration resource + const endpointConfig = new CfnEndpointConfig(this, 'EndpointConfig', { + kmsKeyId: (props.encryptionKey) ? props.encryptionKey.keyArn : undefined, + endpointConfigName: this.physicalName, + productionVariants: cdk.Lazy.any({ produce: () => this.renderInstanceProductionVariants() }), + }); + this.endpointConfigName = this.getResourceNameAttribute(endpointConfig.attrEndpointConfigName); + this.endpointConfigArn = this.getResourceArnAttribute(endpointConfig.ref, { + service: 'sagemaker', + resource: 'endpoint-config', + resourceName: this.physicalName, + }); + } + + /** + * Add production variant to the endpoint configuration. + * + * @param props The properties of a production variant to add. + */ + public addInstanceProductionVariant(props: InstanceProductionVariantProps): void { + if (props.variantName in this._instanceProductionVariants) { + throw new Error(`There is already a Production Variant with name '${props.variantName}'`); + } + this.validateProps(props); + this._instanceProductionVariants[props.variantName] = { + acceleratorType: props.acceleratorType, + initialInstanceCount: props.initialInstanceCount || 1, + initialVariantWeight: props.initialVariantWeight || 1.0, + instanceType: props.instanceType || InstanceType.T2_MEDIUM, + modelName: props.model.modelName, + variantName: props.variantName, + }; + } + + /** + * Get instance production variants associated with endpoint configuration. + */ + public get instanceProductionVariants(): InstanceProductionVariant[] { + return Object.values(this._instanceProductionVariants); + } + + /** + * Find instance production variant based on variant name + * @param name Variant name from production variant + */ + public findInstanceProductionVariant(name: string): InstanceProductionVariant { + const ret = this._instanceProductionVariants[name]; + if (!ret) { + throw new Error(`No variant with name: '${name}'`); + } + return ret; + } + + private validateProductionVariants(): void { + // validate number of production variants + if (this.instanceProductionVariants.length < 1) { + throw new Error('Must configure at least 1 production variant'); + } else if (this.instanceProductionVariants.length > 10) { + throw new Error('Can\'t have more than 10 production variants'); + } + } + + private validateProps(props: InstanceProductionVariantProps): void { + const errors: string[] = []; + + // check instance count is greater than zero + if (props.initialInstanceCount !== undefined && props.initialInstanceCount < 1) { + errors.push('Must have at least one instance'); + } + + // check variant weight is not negative + if (props.initialVariantWeight && props.initialVariantWeight < 0) { + errors.push('Cannot have negative variant weight'); + } + + // check environment compatibility with model + const model = props.model; + if (!sameEnvDimension(model.env.account, this.env.account)) { + errors.push(`Cannot use model in account ${model.env.account} for endpoint configuration in account ${this.env.account}`); + } else if (!sameEnvDimension(model.env.region, this.env.region)) { + errors.push(`Cannot use model in region ${model.env.region} for endpoint configuration in region ${this.env.region}`); + } + + if (errors.length > 0) { + throw new Error(`Invalid Production Variant Props: ${errors.join(EOL)}`); + } + } + + /** + * Render the list of instance production variants. + */ + private renderInstanceProductionVariants(): CfnEndpointConfig.ProductionVariantProperty[] { + this.validateProductionVariants(); + return this.instanceProductionVariants.map( v => ({ + acceleratorType: v.acceleratorType?.toString(), + initialInstanceCount: v.initialInstanceCount, + initialVariantWeight: v.initialVariantWeight, + instanceType: v.instanceType.toString(), + modelName: v.modelName, + variantName: v.variantName, + }) ); + } + +} diff --git a/packages/@aws-cdk/aws-sagemaker/lib/index.ts b/packages/@aws-cdk/aws-sagemaker/lib/index.ts index 576ff2f199e4b..a764740e5ab70 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/index.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/index.ts @@ -1,4 +1,7 @@ +export * from './accelerator-type'; export * from './container-image'; +export * from './endpoint-config'; +export * from './instance-type'; export * from './model'; export * from './model-data'; diff --git a/packages/@aws-cdk/aws-sagemaker/lib/instance-type.ts b/packages/@aws-cdk/aws-sagemaker/lib/instance-type.ts new file mode 100644 index 0000000000000..08ee041b278bf --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/lib/instance-type.ts @@ -0,0 +1,453 @@ +import * as cdk from '@aws-cdk/core'; + +/** + * Supported instance types for SageMaker instance-based production variants. + */ +export class InstanceType { + /** + * ml.c4.2xlarge + */ + public static readonly C4_2XLARGE = InstanceType.of('ml.c4.2xlarge'); + + /** + * ml.c4.4xlarge + */ + public static readonly C4_4XLARGE = InstanceType.of('ml.c4.4xlarge'); + + /** + * ml.c4.8xlarge + */ + public static readonly C4_8XLARGE = InstanceType.of('ml.c4.8xlarge'); + + /** + * ml.c4.large + */ + public static readonly C4_LARGE = InstanceType.of('ml.c4.large'); + + /** + * ml.c4.xlarge + */ + public static readonly C4_XLARGE = InstanceType.of('ml.c4.xlarge'); + + /** + * ml.c5.18xlarge + */ + public static readonly C5_18XLARGE = InstanceType.of('ml.c5.18xlarge'); + + /** + * ml.c5.2xlarge + */ + public static readonly C5_2XLARGE = InstanceType.of('ml.c5.2xlarge'); + + /** + * ml.c5.4xlarge + */ + public static readonly C5_4XLARGE = InstanceType.of('ml.c5.4xlarge'); + + /** + * ml.c5.9xlarge + */ + public static readonly C5_9XLARGE = InstanceType.of('ml.c5.9xlarge'); + + /** + * ml.c5.large + */ + public static readonly C5_LARGE = InstanceType.of('ml.c5.large'); + + /** + * ml.c5.xlarge + */ + public static readonly C5_XLARGE = InstanceType.of('ml.c5.xlarge'); + + /** + * ml.c5d.18xlarge + */ + public static readonly C5D_18XLARGE = InstanceType.of('ml.c5d.18xlarge'); + + /** + * ml.c5d.2xlarge + */ + public static readonly C5D_2XLARGE = InstanceType.of('ml.c5d.2xlarge'); + + /** + * ml.c5d.4xlarge + */ + public static readonly C5D_4XLARGE = InstanceType.of('ml.c5d.4xlarge'); + + /** + * ml.c5d.9xlarge + */ + public static readonly C5D_9XLARGE = InstanceType.of('ml.c5d.9xlarge'); + + /** + * ml.c5d.large + */ + public static readonly C5D_LARGE = InstanceType.of('ml.c5d.large'); + + /** + * ml.c5d.xlarge + */ + public static readonly C5D_XLARGE = InstanceType.of('ml.c5d.xlarge'); + + /** + * ml.c6i.12xlarge + */ + public static readonly C6I_12XLARGE = InstanceType.of('ml.c6i.12xlarge'); + + /** + * ml.c6i.16xlarge + */ + public static readonly C6I_16XLARGE = InstanceType.of('ml.c6i.16xlarge'); + + /** + * ml.c6i.24xlarge + */ + public static readonly C6I_24XLARGE = InstanceType.of('ml.c6i.24xlarge'); + + /** + * ml.c6i.2xlarge + */ + public static readonly C6I_2XLARGE = InstanceType.of('ml.c6i.2xlarge'); + + /** + * ml.c6i.32xlarge + */ + public static readonly C6I_32XLARGE = InstanceType.of('ml.c6i.32xlarge'); + + /** + * ml.c6i.4xlarge + */ + public static readonly C6I_4XLARGE = InstanceType.of('ml.c6i.4xlarge'); + + /** + * ml.c6i.8xlarge + */ + public static readonly C6I_8XLARGE = InstanceType.of('ml.c6i.8xlarge'); + + /** + * ml.c6i.large + */ + public static readonly C6I_LARGE = InstanceType.of('ml.c6i.large'); + + /** + * ml.c6i.xlarge + */ + public static readonly C6I_XLARGE = InstanceType.of('ml.c6i.xlarge'); + + /** + * ml.g4dn.12xlarge + */ + public static readonly G4DN_12XLARGE = InstanceType.of('ml.g4dn.12xlarge'); + + /** + * ml.g4dn.16xlarge + */ + public static readonly G4DN_16XLARGE = InstanceType.of('ml.g4dn.16xlarge'); + + /** + * ml.g4dn.2xlarge + */ + public static readonly G4DN_2XLARGE = InstanceType.of('ml.g4dn.2xlarge'); + + /** + * ml.g4dn.4xlarge + */ + public static readonly G4DN_4XLARGE = InstanceType.of('ml.g4dn.4xlarge'); + + /** + * ml.g4dn.8xlarge + */ + public static readonly G4DN_8XLARGE = InstanceType.of('ml.g4dn.8xlarge'); + + /** + * ml.g4dn.xlarge + */ + public static readonly G4DN_XLARGE = InstanceType.of('ml.g4dn.xlarge'); + + /** + * ml.g5.12xlarge + */ + public static readonly G5_12XLARGE = InstanceType.of('ml.g5.12xlarge'); + + /** + * ml.g5.16xlarge + */ + public static readonly G5_16XLARGE = InstanceType.of('ml.g5.16xlarge'); + + /** + * ml.g5.24xlarge + */ + public static readonly G5_24XLARGE = InstanceType.of('ml.g5.24xlarge'); + + /** + * ml.g5.2xlarge + */ + public static readonly G5_2XLARGE = InstanceType.of('ml.g5.2xlarge'); + + /** + * ml.g5.48xlarge + */ + public static readonly G5_48XLARGE = InstanceType.of('ml.g5.48xlarge'); + + /** + * ml.g5.4xlarge + */ + public static readonly G5_4XLARGE = InstanceType.of('ml.g5.4xlarge'); + + /** + * ml.g5.8xlarge + */ + public static readonly G5_8XLARGE = InstanceType.of('ml.g5.8xlarge'); + + /** + * ml.g5.xlarge + */ + public static readonly G5_XLARGE = InstanceType.of('ml.g5.xlarge'); + + /** + * ml.inf1.24xlarge + */ + public static readonly INF1_24XLARGE = InstanceType.of('ml.inf1.24xlarge'); + + /** + * ml.inf1.2xlarge + */ + public static readonly INF1_2XLARGE = InstanceType.of('ml.inf1.2xlarge'); + + /** + * ml.inf1.6xlarge + */ + public static readonly INF1_6XLARGE = InstanceType.of('ml.inf1.6xlarge'); + + /** + * ml.inf1.xlarge + */ + public static readonly INF1_XLARGE = InstanceType.of('ml.inf1.xlarge'); + + /** + * ml.m4.10xlarge + */ + public static readonly M4_10XLARGE = InstanceType.of('ml.m4.10xlarge'); + + /** + * ml.m4.16xlarge + */ + public static readonly M4_16XLARGE = InstanceType.of('ml.m4.16xlarge'); + + /** + * ml.m4.2xlarge + */ + public static readonly M4_2XLARGE = InstanceType.of('ml.m4.2xlarge'); + + /** + * ml.m4.4xlarge + */ + public static readonly M4_4XLARGE = InstanceType.of('ml.m4.4xlarge'); + + /** + * ml.m4.xlarge + */ + public static readonly M4_XLARGE = InstanceType.of('ml.m4.xlarge'); + + /** + * ml.m5.12xlarge + */ + public static readonly M5_12XLARGE = InstanceType.of('ml.m5.12xlarge'); + + /** + * ml.m5.24xlarge + */ + public static readonly M5_24XLARGE = InstanceType.of('ml.m5.24xlarge'); + + /** + * ml.m5.2xlarge + */ + public static readonly M5_2XLARGE = InstanceType.of('ml.m5.2xlarge'); + + /** + * ml.m5.4xlarge + */ + public static readonly M5_4XLARGE = InstanceType.of('ml.m5.4xlarge'); + + /** + * ml.m5.large + */ + public static readonly M5_LARGE = InstanceType.of('ml.m5.large'); + + /** + * ml.m5.xlarge + */ + public static readonly M5_XLARGE = InstanceType.of('ml.m5.xlarge'); + + /** + * ml.m5d.12xlarge + */ + public static readonly M5D_12XLARGE = InstanceType.of('ml.m5d.12xlarge'); + + /** + * ml.m5d.24xlarge + */ + public static readonly M5D_24XLARGE = InstanceType.of('ml.m5d.24xlarge'); + + /** + * ml.m5d.2xlarge + */ + public static readonly M5D_2XLARGE = InstanceType.of('ml.m5d.2xlarge'); + + /** + * ml.m5d.4xlarge + */ + public static readonly M5D_4XLARGE = InstanceType.of('ml.m5d.4xlarge'); + + /** + * ml.m5d.large + */ + public static readonly M5D_LARGE = InstanceType.of('ml.m5d.large'); + + /** + * ml.m5d.xlarge + */ + public static readonly M5D_XLARGE = InstanceType.of('ml.m5d.xlarge'); + + /** + * ml.p2.16xlarge + */ + public static readonly P2_16XLARGE = InstanceType.of('ml.p2.16xlarge'); + + /** + * ml.p2.8xlarge + */ + public static readonly P2_8XLARGE = InstanceType.of('ml.p2.8xlarge'); + + /** + * ml.p2.xlarge + */ + public static readonly P2_XLARGE = InstanceType.of('ml.p2.xlarge'); + + /** + * ml.p3.16xlarge + */ + public static readonly P3_16XLARGE = InstanceType.of('ml.p3.16xlarge'); + + /** + * ml.p3.2xlarge + */ + public static readonly P3_2XLARGE = InstanceType.of('ml.p3.2xlarge'); + + /** + * ml.p3.8xlarge + */ + public static readonly P3_8XLARGE = InstanceType.of('ml.p3.8xlarge'); + + /** + * ml.p4d.24xlarge + */ + public static readonly P4D_24XLARGE = InstanceType.of('ml.p4d.24xlarge'); + + /** + * ml.r5.12xlarge + */ + public static readonly R5_12XLARGE = InstanceType.of('ml.r5.12xlarge'); + + /** + * ml.r5.24xlarge + */ + public static readonly R5_24XLARGE = InstanceType.of('ml.r5.24xlarge'); + + /** + * ml.r5.2xlarge + */ + public static readonly R5_2XLARGE = InstanceType.of('ml.r5.2xlarge'); + + /** + * ml.r5.4xlarge + */ + public static readonly R5_4XLARGE = InstanceType.of('ml.r5.4xlarge'); + + /** + * ml.r5.large + */ + public static readonly R5_LARGE = InstanceType.of('ml.r5.large'); + + /** + * ml.r5.xlarge + */ + public static readonly R5_XLARGE = InstanceType.of('ml.r5.xlarge'); + + /** + * ml.r5d.12xlarge + */ + public static readonly R5D_12XLARGE = InstanceType.of('ml.r5d.12xlarge'); + + /** + * ml.r5d.24xlarge + */ + public static readonly R5D_24XLARGE = InstanceType.of('ml.r5d.24xlarge'); + + /** + * ml.r5d.2xlarge + */ + public static readonly R5D_2XLARGE = InstanceType.of('ml.r5d.2xlarge'); + + /** + * ml.r5d.4xlarge + */ + public static readonly R5D_4XLARGE = InstanceType.of('ml.r5d.4xlarge'); + + /** + * ml.r5d.large + */ + public static readonly R5D_LARGE = InstanceType.of('ml.r5d.large'); + + /** + * ml.r5d.xlarge + */ + public static readonly R5D_XLARGE = InstanceType.of('ml.r5d.xlarge'); + + /** + * ml.t2.2xlarge + */ + public static readonly T2_2XLARGE = InstanceType.of('ml.t2.2xlarge'); + + /** + * ml.t2.large + */ + public static readonly T2_LARGE = InstanceType.of('ml.t2.large'); + + /** + * ml.t2.medium + */ + public static readonly T2_MEDIUM = InstanceType.of('ml.t2.medium'); + + /** + * ml.t2.xlarge + */ + public static readonly T2_XLARGE = InstanceType.of('ml.t2.xlarge'); + + /** + * Builds an InstanceType from a given string or token (such as a CfnParameter). + * @param instanceType An instance type as string + * @returns A strongly typed InstanceType + */ + public static of(instanceType: string): InstanceType { + return new InstanceType(instanceType); + } + + private readonly instanceTypeIdentifier: string; + + constructor(instanceType: string) { + if (cdk.Token.isUnresolved(instanceType) || instanceType.startsWith('ml.')) { + this.instanceTypeIdentifier = instanceType; + } else { + throw new Error(`instance type must start with 'ml.'; (got ${instanceType})`); + } + } + + /** + * Return the instance type as a string + * @returns The instance type as a string + */ + public toString(): string { + return this.instanceTypeIdentifier; + } +} diff --git a/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts b/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts index 4057c92a2d717..7940e76a7f730 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts @@ -1,4 +1,5 @@ import * as crypto from 'crypto'; +import * as cdk from '@aws-cdk/core'; /** * Generates a hash from the provided string for the purposes of avoiding construct ID collision @@ -11,3 +12,15 @@ export function hashcode(s: string): string { hash.update(s); return hash.digest('hex'); } + +/** + * Whether two strings probably contain the same environment dimension (region or account). + * + * Used to compare either accounts or regions, and also returns true if both + * are unresolved (in which case both are expted to be "current region" or "current account"). + * @param dim1 The first dimension to compare + * @param dim2 The second dimension to compare + */ +export function sameEnvDimension(dim1: string, dim2: string): boolean { + return [cdk.TokenComparison.SAME, cdk.TokenComparison.BOTH_UNRESOLVED].includes(cdk.Token.compareStrings(dim1, dim2)); +} diff --git a/packages/@aws-cdk/aws-sagemaker/package.json b/packages/@aws-cdk/aws-sagemaker/package.json index c5783894fb7f8..55e81fd8a056f 100644 --- a/packages/@aws-cdk/aws-sagemaker/package.json +++ b/packages/@aws-cdk/aws-sagemaker/package.json @@ -96,6 +96,7 @@ "@aws-cdk/aws-ecr": "0.0.0", "@aws-cdk/aws-ecr-assets": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/core": "0.0.0", @@ -107,6 +108,7 @@ "@aws-cdk/aws-ecr": "0.0.0", "@aws-cdk/aws-ecr-assets": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-kms": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts b/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts new file mode 100644 index 0000000000000..7ddcafeafe5c1 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts @@ -0,0 +1,359 @@ +import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; +import * as sagemaker from '../lib'; + +describe('When synthesizing a stack containing an EndpointConfig', () => { + test('with more than 10 production variants, an exception is thrown', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app); + const model = sagemaker.Model.fromModelName(stack, 'Model', 'model'); + const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { + instanceProductionVariants: [{ + variantName: 'variant', + model, + }], + }); + for (let i = 0; i < 10; i++) { + endpointConfig.addInstanceProductionVariant({ variantName: `variant-${i}`, model }); + } + + // WHEN + const when = () => app.synth(); + + // THEN + expect(when).toThrow(/Can\'t have more than 10 production variants/); + }); + + test('with no production variants, an exception is thrown', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app); + new sagemaker.EndpointConfig(stack, 'EndpointConfig'); + + // WHEN + const when = () => app.synth(); + + // THEN + expect(when).toThrow(/Must configure at least 1 production variant/); + }); +}); + +describe('When adding a production variant to an EndpointConfig', () => { + test('with too few instances specified, an exception is thrown', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = sagemaker.Model.fromModelName(stack, 'Model', 'model'); + const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); + + // WHEN + const when = () => + endpointConfig.addInstanceProductionVariant({ + variantName: 'new-variant', + model, + initialInstanceCount: 0, + }); + + // THEN + expect(when).toThrow(/Invalid Production Variant Props: Must have at least one instance/); + }); + + test('with a negative weight, an exception is thrown', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = sagemaker.Model.fromModelName(stack, 'Model', 'model'); + const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); + + // WHEN + const when = () => + endpointConfig.addInstanceProductionVariant({ + variantName: 'new-variant', + model, + initialVariantWeight: -1, + }); + + // THEN + expect(when).toThrow(/Invalid Production Variant Props: Cannot have negative variant weight/); + }); + + test('with a duplicate variant name, an exception is thrown', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = sagemaker.Model.fromModelName(stack, 'Model', 'model'); + const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); + + // WHEN + const when = () => endpointConfig.addInstanceProductionVariant({ variantName: 'variant', model }); + + // THEN + expect(when).toThrow(/There is already a Production Variant with name 'variant'/); + }); +}); + +describe('When searching an EndpointConfig for a production variant', () => { + test('that exists, the variant is returned', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = sagemaker.Model.fromModelName(stack, 'Model', 'model'); + const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); + + // WHEN + const variant = endpointConfig.findInstanceProductionVariant('variant'); + + // THEN + expect(variant.variantName).toEqual('variant'); + }); + + test('that does not exist, an exception is thrown', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = sagemaker.Model.fromModelName(stack, 'Model', 'model'); + const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); + + // WHEN + const when = () => endpointConfig.findInstanceProductionVariant('missing-variant'); + + // THEN + expect(when).toThrow(/No variant with name: 'missing-variant'/); + }); +}); + +test('When importing an endpoint configuration by ARN, the name is determined correctly', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const endpointConfig = sagemaker.EndpointConfig.fromEndpointConfigArn(stack, 'EndpointConfig', 'arn:aws:sagemaker:us-west-2:123456789012:endpoint-config/my-name'); + + // THEN + expect(endpointConfig.endpointConfigName).toEqual('my-name'); +}); + +test('When importing an endpoint configuration by name, the ARN is constructed correctly', () => { + // GIVEN + const stack = new cdk.Stack(undefined, undefined, { + env: + { + region: 'us-west-2', + account: '123456789012', + }, + }); + + // WHEN + const endpointConfig = sagemaker.EndpointConfig.fromEndpointConfigName(stack, 'EndpointConfig', 'my-name'); + + // THEN + expect(endpointConfig.endpointConfigArn).toMatch(/arn:.+:sagemaker:us-west-2:123456789012:endpoint-config\/my-name/); +}); + +describe('When sharing a model from an origin stack with a destination stack', () => { + describe('which represents an owned Model instance', () => { + test('across stack account boundaries, synthesis fails', () => { + // GIVEN + const app = new cdk.App(); + const originStack = new cdk.Stack(app, 'OriginStack', { + env: + { + region: 'us-west-2', + account: '123456789012', + }, + }); + const originStackModel = new sagemaker.Model(originStack, 'MyModel', { + modelName: 'explicitly-named-model', + containers: [{ + image: sagemaker.ContainerImage.fromAsset(path.join(__dirname, 'test-image')), + }], + }); + const destinationStack = new cdk.Stack(app, 'DestinationStack', { + env: + { + region: 'us-west-2', + account: '123456789013', + }, + }); + + // WHEN + const when = () => + new sagemaker.EndpointConfig(destinationStack, 'MyEndpointConfig', { + instanceProductionVariants: [{ + variantName: 'my-variant', + model: originStackModel, + }], + }); + + // THEN + expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 123456789013/); + }); + + test('across stack region boundaries, synthesis fails', () => { + // GIVEN + const app = new cdk.App(); + const originStack = new cdk.Stack(app, 'OriginStack', { + env: + { + region: 'us-west-2', + account: '123456789012', + }, + }); + const originStackModel = new sagemaker.Model(originStack, 'MyModel', { + modelName: 'explicitly-named-model', + containers: [{ + image: sagemaker.ContainerImage.fromAsset(path.join(__dirname, 'test-image')), + }], + }); + const destinationStack = new cdk.Stack(app, 'DestinationStack', { + env: + { + region: 'us-east-1', + account: '123456789012', + }, + }); + + // WHEN + const when = () => + new sagemaker.EndpointConfig(destinationStack, 'MyEndpointConfig', { + instanceProductionVariants: [{ + variantName: 'my-variant', + model: originStackModel, + }], + }); + + // THEN + expect(when).toThrow(/Cannot use model in region us-west-2 for endpoint configuration in region us-east-1/); + }); + }); + + describe('which represents an unowned IModel instance', () => { + describe('imported by name', () => { + test('across stack account boundaries, synthesis fails', () => { + // GIVEN + const app = new cdk.App(); + const originStack = new cdk.Stack(app, 'OriginStack', { + env: + { + region: 'us-west-2', + account: '123456789012', + }, + }); + const originStackModel = sagemaker.Model.fromModelName(originStack, 'MyModel', 'explicitly-named-model'); + const destinationStack = new cdk.Stack(app, 'DestinationStack', { + env: + { + region: 'us-west-2', + account: '123456789013', + }, + }); + + // WHEN + const when = () => + new sagemaker.EndpointConfig(destinationStack, 'MyEndpointConfig', { + instanceProductionVariants: [{ + variantName: 'my-variant', + model: originStackModel, + }], + }); + + // THEN + expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 123456789013/); + }); + + test('across stack region boundaries, synthesis fails', () => { + // GIVEN + const app = new cdk.App(); + const originStack = new cdk.Stack(app, 'OriginStack', { + env: + { + region: 'us-west-2', + account: '123456789012', + }, + }); + const originStackModel = sagemaker.Model.fromModelName(originStack, 'MyModel', 'explicitly-named-model'); + const destinationStack = new cdk.Stack(app, 'DestinationStack', { + env: + { + region: 'us-east-1', + account: '123456789012', + }, + }); + + // WHEN + const when = () => + new sagemaker.EndpointConfig(destinationStack, 'MyEndpointConfig', { + instanceProductionVariants: [{ + variantName: 'my-variant', + model: originStackModel, + }], + }); + + // THEN + expect(when).toThrow(/Cannot use model in region us-west-2 for endpoint configuration in region us-east-1/); + }); + }); + + describe('imported by ARN', () => { + test('in a different account than both stacks, synthesis fails', () => { + // GIVEN + const app = new cdk.App(); + const originStack = new cdk.Stack(app, 'OriginStack', { + env: + { + region: 'us-west-2', + account: '123456789013', + }, + }); + const originStackModel = sagemaker.Model.fromModelArn(originStack, 'MyModel', 'arn:aws:sagemaker:us-west-2:123456789012:endpoint-config/explicitly-named-model'); + const destinationStack = new cdk.Stack(app, 'DestinationStack', { + env: + { + region: 'us-west-2', + account: '123456789013', + }, + }); + + // WHEN + const when = () => + new sagemaker.EndpointConfig(destinationStack, 'MyEndpointConfig', { + instanceProductionVariants: [{ + variantName: 'my-variant', + model: originStackModel, + }], + }); + + // THEN + expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 123456789013/); + }); + + test('in a different region than both stacks, synthesis fails', () => { + // GIVEN + const app = new cdk.App(); + const originStack = new cdk.Stack(app, 'OriginStack', { + env: + { + region: 'us-east-1', + account: '123456789012', + }, + }); + const originStackModel = sagemaker.Model.fromModelArn(originStack, 'MyModel', 'arn:aws:sagemaker:us-west-2:123456789012:endpoint-config/explicitly-named-model'); + const destinationStack = new cdk.Stack(app, 'DestinationStack', { + env: + { + region: 'us-east-1', + account: '123456789012', + }, + }); + + // WHEN + const when = () => + new sagemaker.EndpointConfig(destinationStack, 'MyEndpointConfig', { + instanceProductionVariants: [{ + variantName: 'my-variant', + model: originStackModel, + }], + }); + + // THEN + expect(when).toThrow(/Cannot use model in region us-west-2 for endpoint configuration in region us-east-1/); + }); + }); + }); +}); diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.tar.gz b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..af6ae9c76e414cfcae4218a758102afb0b332d08 GIT binary patch literal 385 zcmV-{0e=1;iwFp&<1ttfu zQ!-cfNLr`Ia^xb{xL~UwggB)Thg?{o!YT_{kEuAI+H3r", + "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-sagemaker/test/integ.endpoint-config.js.snapshot/cdk.out b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integ.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integ.json new file mode 100644 index 0000000000000..db28d51fce499 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "integtest-endpointconfig/DefaultTest": { + "stacks": [ + "aws-cdk-sagemaker-endpointconfig" + ], + "assertionStack": "integtest-endpointconfig/DefaultTest/DeployAssert", + "assertionStackName": "integtestendpointconfigDefaultTestDeployAssert8D52A281" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.assets.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.assets.json new file mode 100644 index 0000000000000..f590921ea8acf --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integtestendpointconfigDefaultTestDeployAssert8D52A281.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-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.template.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/integtestendpointconfigDefaultTestDeployAssert8D52A281.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-sagemaker/test/integ.endpoint-config.js.snapshot/manifest.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/manifest.json new file mode 100644 index 0000000000000..21793dce5cc2a --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/manifest.json @@ -0,0 +1,291 @@ +{ + "version": "21.0.0", + "artifacts": { + "aws-cdk-sagemaker-endpointconfig.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-sagemaker-endpointconfig.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-sagemaker-endpointconfig": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-sagemaker-endpointconfig.template.json", + "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}/16ab84c598574688eb95944b09c42d17b927d3f0a4725f9a9ccafcce2aefdf53.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-sagemaker-endpointconfig.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": [ + "aws-cdk-sagemaker-endpointconfig.assets" + ], + "metadata": { + "/aws-cdk-sagemaker-endpointconfig/VPC/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCB9E5F0B4" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1SubnetB4246D30" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableFEE4B781" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableAssociation0B0896DC" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1DefaultRoute91CEF279" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1EIP6AD938E8" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1NATGatewayE0556630" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2Subnet74179F39" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTable6F1A15F1" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTableAssociation5A808732" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2DefaultRouteB7481BBA" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2EIP4947BC00" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2NATGateway3C070193" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1Subnet8BCA10E0" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableBE8A6027" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableAssociation347902D1" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1DefaultRouteAE1D6490" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTable0A19E10E" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTableAssociation0C73D413" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2DefaultRouteF4F5CFD2" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCIGWB7E252D3" + } + ], + "/aws-cdk-sagemaker-endpointconfig/VPC/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCVPCGW99B986DC" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithArtifactAndVpcSecurityGroupB499C626" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithArtifactAndVpcRole6BA49FD3" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithArtifactAndVpcRoleDefaultPolicyC77E2AFA" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Model": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithArtifactAndVpcModel30604F15" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithoutArtifactAndVpcRole10D89F15" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithoutArtifactAndVpcRoleDefaultPolicy88BAF094" + } + ], + "/aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Model": [ + { + "type": "aws:cdk:logicalId", + "data": "ModelWithoutArtifactAndVpcModel9A8AD144" + } + ], + "/aws-cdk-sagemaker-endpointconfig/EndpointConfig/EndpointConfig": [ + { + "type": "aws:cdk:logicalId", + "data": "EndpointConfigFD7B6F91" + } + ], + "/aws-cdk-sagemaker-endpointconfig/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-sagemaker-endpointconfig/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-sagemaker-endpointconfig" + }, + "integtestendpointconfigDefaultTestDeployAssert8D52A281.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integtestendpointconfigDefaultTestDeployAssert8D52A281.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integtestendpointconfigDefaultTestDeployAssert8D52A281": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integtestendpointconfigDefaultTestDeployAssert8D52A281.template.json", + "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": [ + "integtestendpointconfigDefaultTestDeployAssert8D52A281.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": [ + "integtestendpointconfigDefaultTestDeployAssert8D52A281.assets" + ], + "metadata": { + "/integtest-endpointconfig/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integtest-endpointconfig/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integtest-endpointconfig/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/tree.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/tree.json new file mode 100644 index 0000000000000..7dd71dcb0e6e4 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/tree.json @@ -0,0 +1,1259 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-sagemaker-endpointconfig": { + "id": "aws-cdk-sagemaker-endpointconfig", + "path": "aws-cdk-sagemaker-endpointconfig", + "children": { + "VPC": { + "id": "VPC", + "path": "aws-cdk-sagemaker-endpointconfig/VPC", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-sagemaker-endpointconfig/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "aws-cdk-sagemaker-endpointconfig/VPC/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Ref": "VPCB9E5F0B4" + }, + "internetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.Vpc", + "version": "0.0.0" + } + }, + "ModelWithArtifactAndVpc": { + "id": "ModelWithArtifactAndVpc", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc", + "children": { + "SecurityGroup": { + "id": "SecurityGroup", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/SecurityGroup", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Role", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "sagemaker.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonSageMakerFullAccess" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::Sub": "cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":s3:::", + { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "ModelWithArtifactAndVpcRoleDefaultPolicyC77E2AFA", + "roles": [ + { + "Ref": "ModelWithArtifactAndVpcRole6BA49FD3" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "ModelImage7ffa8020b99fe9d130a903251c36866d": { + "id": "ModelImage7ffa8020b99fe9d130a903251c36866d", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/ModelImage7ffa8020b99fe9d130a903251c36866d", + "children": { + "Staging": { + "id": "Staging", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/ModelImage7ffa8020b99fe9d130a903251c36866d/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Repository": { + "id": "Repository", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/ModelImage7ffa8020b99fe9d130a903251c36866d/Repository", + "constructInfo": { + "fqn": "@aws-cdk/aws-ecr.RepositoryBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ecr-assets.DockerImageAsset", + "version": "0.0.0" + } + }, + "ModelData412d61f9c984d1aff5ee358daf994d58": { + "id": "ModelData412d61f9c984d1aff5ee358daf994d58", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/ModelData412d61f9c984d1aff5ee358daf994d58", + "children": { + "Stage": { + "id": "Stage", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/ModelData412d61f9c984d1aff5ee358daf994d58/Stage", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/ModelData412d61f9c984d1aff5ee358daf994d58/AssetBucket", + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3-assets.Asset", + "version": "0.0.0" + } + }, + "Model": { + "id": "Model", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithArtifactAndVpc/Model", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SageMaker::Model", + "aws:cdk:cloudformation:props": { + "executionRoleArn": { + "Fn::GetAtt": [ + "ModelWithArtifactAndVpcRole6BA49FD3", + "Arn" + ] + }, + "primaryContainer": { + "image": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a" + }, + "modelDataUrl": { + "Fn::Sub": "https://s3.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.gz" + } + }, + "vpcConfig": { + "subnets": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ], + "securityGroupIds": [ + { + "Fn::GetAtt": [ + "ModelWithArtifactAndVpcSecurityGroupB499C626", + "GroupId" + ] + } + ] + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-sagemaker.CfnModel", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-sagemaker.Model", + "version": "0.0.0" + } + }, + "ModelWithoutArtifactAndVpc": { + "id": "ModelWithoutArtifactAndVpc", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc", + "children": { + "Role": { + "id": "Role", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Role", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "sagemaker.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonSageMakerFullAccess" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::Sub": "cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}" + } + ] + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "ModelWithoutArtifactAndVpcRoleDefaultPolicy88BAF094", + "roles": [ + { + "Ref": "ModelWithoutArtifactAndVpcRole10D89F15" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Model": { + "id": "Model", + "path": "aws-cdk-sagemaker-endpointconfig/ModelWithoutArtifactAndVpc/Model", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SageMaker::Model", + "aws:cdk:cloudformation:props": { + "executionRoleArn": { + "Fn::GetAtt": [ + "ModelWithoutArtifactAndVpcRole10D89F15", + "Arn" + ] + }, + "primaryContainer": { + "image": { + "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}/cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}:442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a" + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-sagemaker.CfnModel", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-sagemaker.Model", + "version": "0.0.0" + } + }, + "EndpointConfig": { + "id": "EndpointConfig", + "path": "aws-cdk-sagemaker-endpointconfig/EndpointConfig", + "children": { + "EndpointConfig": { + "id": "EndpointConfig", + "path": "aws-cdk-sagemaker-endpointconfig/EndpointConfig/EndpointConfig", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SageMaker::EndpointConfig", + "aws:cdk:cloudformation:props": { + "productionVariants": [ + { + "initialInstanceCount": 1, + "initialVariantWeight": 1, + "instanceType": "ml.m5.large", + "modelName": { + "Fn::GetAtt": [ + "ModelWithArtifactAndVpcModel30604F15", + "ModelName" + ] + }, + "variantName": "firstVariant" + }, + { + "initialInstanceCount": 1, + "initialVariantWeight": 1, + "instanceType": "ml.t2.medium", + "modelName": { + "Fn::GetAtt": [ + "ModelWithArtifactAndVpcModel30604F15", + "ModelName" + ] + }, + "variantName": "secondVariant" + }, + { + "initialInstanceCount": 1, + "initialVariantWeight": 2, + "instanceType": "ml.t2.medium", + "modelName": { + "Fn::GetAtt": [ + "ModelWithoutArtifactAndVpcModel9A8AD144", + "ModelName" + ] + }, + "variantName": "thirdVariant" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-sagemaker.CfnEndpointConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-sagemaker.EndpointConfig", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-sagemaker-endpointconfig/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-sagemaker-endpointconfig/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "integtest-endpointconfig": { + "id": "integtest-endpointconfig", + "path": "integtest-endpointconfig", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integtest-endpointconfig/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integtest-endpointconfig/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.140" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integtest-endpointconfig/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integtest-endpointconfig/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integtest-endpointconfig/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.140" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.ts new file mode 100644 index 0000000000000..c5f982a7d1297 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.ts @@ -0,0 +1,78 @@ +import * as path from 'path'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import * as sagemaker from '../lib'; + +/* + * Stack verification steps: + * aws sagemaker describe-endpoint-config --endpoint-config-name + * + * The above command will result in the following output. + * + * { + * "EndpointConfigName": "EndpointConfig...", + * "EndpointConfigArn": "arn:aws:sagemaker:...", + * "ProductionVariants": [ + * { + * "VariantName": "firstVariant", + * "ModelName": "ModelWithArtifactAndVpcModel...", + * "InitialInstanceCount": 1, + * "InstanceType": "ml.m5.large", + * "InitialVariantWeight": 1.0 + * }, + * { + * "VariantName": "secondVariant", + * "ModelName": "ModelWithArtifactAndVpcModel...", + * "InitialInstanceCount": 1, + * "InstanceType": "ml.t2.medium", + * "InitialVariantWeight": 1.0 + * }, + * { + * "VariantName": "thirdVariant", + * "ModelName": "ModelWithoutArtifactAndVpcModel...", + * "InitialInstanceCount": 1, + * "InstanceType": "ml.t2.medium", + * "InitialVariantWeight": 2.0 + * } + * ], + * "CreationTime": "..." + * } + */ + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-sagemaker-endpointconfig'); + +const image = sagemaker.ContainerImage.fromAsset(path.join(__dirname, 'test-image')); +const modelData = sagemaker.ModelData.fromAsset(path.join(__dirname, 'test-artifacts', 'valid-artifact.tar.gz')); + +const modelWithArtifactAndVpc = new sagemaker.Model(stack, 'ModelWithArtifactAndVpc', { + containers: [{ image, modelData }], + vpc: new ec2.Vpc(stack, 'VPC'), +}); +const modelWithoutArtifactAndVpc = new sagemaker.Model(stack, 'ModelWithoutArtifactAndVpc', { + containers: [{ image }], +}); + +const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { + instanceProductionVariants: [ + { + model: modelWithArtifactAndVpc, + variantName: 'firstVariant', + instanceType: sagemaker.InstanceType.M5_LARGE, + }, + { + model: modelWithArtifactAndVpc, + variantName: 'secondVariant', + }, + ], +}); +endpointConfig.addInstanceProductionVariant({ + model: modelWithoutArtifactAndVpc, + variantName: 'thirdVariant', + initialVariantWeight: 2.0, +}); + +new IntegTest(app, 'integtest-endpointconfig', { + testCases: [stack], +}); From 49d49cb8d0f8a2ca7accb5c7eab0082f35eca4b2 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Thu, 10 Nov 2022 09:22:23 -0800 Subject: [PATCH 2/7] Use allow-listed account ID for cross-env tests --- .../aws-sagemaker/test/endpoint-config.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts b/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts index 7ddcafeafe5c1..c98e1437b190a 100644 --- a/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts +++ b/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts @@ -168,7 +168,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( env: { region: 'us-west-2', - account: '123456789013', + account: '234567890123', }, }); @@ -182,7 +182,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( }); // THEN - expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 123456789013/); + expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 234567890123/); }); test('across stack region boundaries, synthesis fails', () => { @@ -240,7 +240,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( env: { region: 'us-west-2', - account: '123456789013', + account: '234567890123', }, }); @@ -254,7 +254,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( }); // THEN - expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 123456789013/); + expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 234567890123/); }); test('across stack region boundaries, synthesis fails', () => { @@ -298,7 +298,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( env: { region: 'us-west-2', - account: '123456789013', + account: '234567890123', }, }); const originStackModel = sagemaker.Model.fromModelArn(originStack, 'MyModel', 'arn:aws:sagemaker:us-west-2:123456789012:endpoint-config/explicitly-named-model'); @@ -306,7 +306,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( env: { region: 'us-west-2', - account: '123456789013', + account: '234567890123', }, }); @@ -320,7 +320,7 @@ describe('When sharing a model from an origin stack with a destination stack', ( }); // THEN - expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 123456789013/); + expect(when).toThrow(/Cannot use model in account 123456789012 for endpoint configuration in account 234567890123/); }); test('in a different region than both stacks, synthesis fails', () => { From d7c8f3fd66d131ff9ebba87b495f2262c045ef91 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Thu, 10 Nov 2022 14:57:10 -0800 Subject: [PATCH 3/7] Specify @default instance using defined type --- packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts index 644e9c35580fe..bf78add1e3dab 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts @@ -68,7 +68,7 @@ export interface InstanceProductionVariantProps extends ProductionVariantProps { /** * Instance type of the production variant. * - * @default - ml.t2.medium instance type. + * @default InstanceType.T2_MEDIUM */ readonly instanceType?: InstanceType; } From 7f7a46fbe0613e19e99342a7123bd0d774f9d706 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Thu, 10 Nov 2022 14:58:32 -0800 Subject: [PATCH 4/7] Fix util.ts doc typo --- packages/@aws-cdk/aws-sagemaker/lib/private/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts b/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts index 7940e76a7f730..3cdbeaf7945f6 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts @@ -17,7 +17,7 @@ export function hashcode(s: string): string { * Whether two strings probably contain the same environment dimension (region or account). * * Used to compare either accounts or regions, and also returns true if both - * are unresolved (in which case both are expted to be "current region" or "current account"). + * are unresolved (in which case both are expected to be "current region" or "current account"). * @param dim1 The first dimension to compare * @param dim2 The second dimension to compare */ From 47159b1eba956437a696ecaaff838f272de21634 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:00:00 -0800 Subject: [PATCH 5/7] Give validateProps a more descriptive name --- packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts index bf78add1e3dab..b219007cc5747 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts @@ -230,7 +230,7 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { if (props.variantName in this._instanceProductionVariants) { throw new Error(`There is already a Production Variant with name '${props.variantName}'`); } - this.validateProps(props); + this.validateInstanceProductionVariantProps(props); this._instanceProductionVariants[props.variantName] = { acceleratorType: props.acceleratorType, initialInstanceCount: props.initialInstanceCount || 1, @@ -269,7 +269,7 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { } } - private validateProps(props: InstanceProductionVariantProps): void { + private validateInstanceProductionVariantProps(props: InstanceProductionVariantProps): void { const errors: string[] = []; // check instance count is greater than zero From 99b2bf0626d17151c1a52c30189d34aa018acb04 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:06:33 -0800 Subject: [PATCH 6/7] Drop all mentions of environment "dimension" --- packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts | 6 +++--- packages/@aws-cdk/aws-sagemaker/lib/private/util.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts index b219007cc5747..093cc7517c8ba 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts @@ -5,7 +5,7 @@ import { Construct } from 'constructs'; import { AcceleratorType } from './accelerator-type'; import { InstanceType } from './instance-type'; import { IModel } from './model'; -import { sameEnvDimension } from './private/util'; +import { sameEnv } from './private/util'; import { CfnEndpointConfig } from './sagemaker.generated'; /** @@ -284,9 +284,9 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { // check environment compatibility with model const model = props.model; - if (!sameEnvDimension(model.env.account, this.env.account)) { + if (!sameEnv(model.env.account, this.env.account)) { errors.push(`Cannot use model in account ${model.env.account} for endpoint configuration in account ${this.env.account}`); - } else if (!sameEnvDimension(model.env.region, this.env.region)) { + } else if (!sameEnv(model.env.region, this.env.region)) { errors.push(`Cannot use model in region ${model.env.region} for endpoint configuration in region ${this.env.region}`); } diff --git a/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts b/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts index 3cdbeaf7945f6..742dd54a1fe2c 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/private/util.ts @@ -14,13 +14,13 @@ export function hashcode(s: string): string { } /** - * Whether two strings probably contain the same environment dimension (region or account). + * Whether two strings probably contain the same environment attribute (region or account). * * Used to compare either accounts or regions, and also returns true if both * are unresolved (in which case both are expected to be "current region" or "current account"). - * @param dim1 The first dimension to compare - * @param dim2 The second dimension to compare + * @param attr1 The first attribute to compare + * @param attr2 The second attribute to compare */ -export function sameEnvDimension(dim1: string, dim2: string): boolean { - return [cdk.TokenComparison.SAME, cdk.TokenComparison.BOTH_UNRESOLVED].includes(cdk.Token.compareStrings(dim1, dim2)); +export function sameEnv(attr1: string, attr2: string): boolean { + return [cdk.TokenComparison.SAME, cdk.TokenComparison.BOTH_UNRESOLVED].includes(cdk.Token.compareStrings(attr1, attr2)); } From a06f4430b06dab0eb452b053e952901f4fae0c95 Mon Sep 17 00:00:00 2001 From: Peter VanLund <792688+petermeansrock@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:22:20 -0800 Subject: [PATCH 7/7] Mark methods used only by Endpoint as @internal --- .../aws-sagemaker/lib/endpoint-config.ts | 26 ++++++++++++------- .../test/endpoint-config.test.ts | 4 +-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts index 093cc7517c8ba..46e33da1fad2a 100644 --- a/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts +++ b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts @@ -96,6 +96,8 @@ interface ProductionVariant { /** * Represents an instance production variant that has been associated with an EndpointConfig. + * + * @internal */ export interface InstanceProductionVariant extends ProductionVariant { /** @@ -198,7 +200,7 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { */ public readonly endpointConfigName: string; - private readonly _instanceProductionVariants: { [key: string]: InstanceProductionVariant; } = {}; + private readonly instanceProductionVariantsByName: { [key: string]: InstanceProductionVariant; } = {}; constructor(scope: Construct, id: string, props: EndpointConfigProps = {}) { super(scope, id, { @@ -227,11 +229,11 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { * @param props The properties of a production variant to add. */ public addInstanceProductionVariant(props: InstanceProductionVariantProps): void { - if (props.variantName in this._instanceProductionVariants) { + if (props.variantName in this.instanceProductionVariantsByName) { throw new Error(`There is already a Production Variant with name '${props.variantName}'`); } this.validateInstanceProductionVariantProps(props); - this._instanceProductionVariants[props.variantName] = { + this.instanceProductionVariantsByName[props.variantName] = { acceleratorType: props.acceleratorType, initialInstanceCount: props.initialInstanceCount || 1, initialVariantWeight: props.initialVariantWeight || 1.0, @@ -243,17 +245,21 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { /** * Get instance production variants associated with endpoint configuration. + * + * @internal */ - public get instanceProductionVariants(): InstanceProductionVariant[] { - return Object.values(this._instanceProductionVariants); + public get _instanceProductionVariants(): InstanceProductionVariant[] { + return Object.values(this.instanceProductionVariantsByName); } /** * Find instance production variant based on variant name * @param name Variant name from production variant + * + * @internal */ - public findInstanceProductionVariant(name: string): InstanceProductionVariant { - const ret = this._instanceProductionVariants[name]; + public _findInstanceProductionVariant(name: string): InstanceProductionVariant { + const ret = this.instanceProductionVariantsByName[name]; if (!ret) { throw new Error(`No variant with name: '${name}'`); } @@ -262,9 +268,9 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { private validateProductionVariants(): void { // validate number of production variants - if (this.instanceProductionVariants.length < 1) { + if (this._instanceProductionVariants.length < 1) { throw new Error('Must configure at least 1 production variant'); - } else if (this.instanceProductionVariants.length > 10) { + } else if (this._instanceProductionVariants.length > 10) { throw new Error('Can\'t have more than 10 production variants'); } } @@ -300,7 +306,7 @@ export class EndpointConfig extends cdk.Resource implements IEndpointConfig { */ private renderInstanceProductionVariants(): CfnEndpointConfig.ProductionVariantProperty[] { this.validateProductionVariants(); - return this.instanceProductionVariants.map( v => ({ + return this._instanceProductionVariants.map( v => ({ acceleratorType: v.acceleratorType?.toString(), initialInstanceCount: v.initialInstanceCount, initialVariantWeight: v.initialVariantWeight, diff --git a/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts b/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts index c98e1437b190a..681db3d5c2aaf 100644 --- a/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts +++ b/packages/@aws-cdk/aws-sagemaker/test/endpoint-config.test.ts @@ -98,7 +98,7 @@ describe('When searching an EndpointConfig for a production variant', () => { const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); // WHEN - const variant = endpointConfig.findInstanceProductionVariant('variant'); + const variant = endpointConfig._findInstanceProductionVariant('variant'); // THEN expect(variant.variantName).toEqual('variant'); @@ -111,7 +111,7 @@ describe('When searching an EndpointConfig for a production variant', () => { const endpointConfig = new sagemaker.EndpointConfig(stack, 'EndpointConfig', { instanceProductionVariants: [{ variantName: 'variant', model }] }); // WHEN - const when = () => endpointConfig.findInstanceProductionVariant('missing-variant'); + const when = () => endpointConfig._findInstanceProductionVariant('missing-variant'); // THEN expect(when).toThrow(/No variant with name: 'missing-variant'/);