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..46e33da1fad2a --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/lib/endpoint-config.ts @@ -0,0 +1,319 @@ +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 { sameEnv } 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 InstanceType.T2_MEDIUM + */ + 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. + * + * @internal + */ +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 instanceProductionVariantsByName: { [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.instanceProductionVariantsByName) { + throw new Error(`There is already a Production Variant with name '${props.variantName}'`); + } + this.validateInstanceProductionVariantProps(props); + this.instanceProductionVariantsByName[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. + * + * @internal + */ + 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.instanceProductionVariantsByName[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 validateInstanceProductionVariantProps(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 (!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 (!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}`); + } + + 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..742dd54a1fe2c 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 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 attr1 The first attribute to compare + * @param attr2 The second attribute to compare + */ +export function sameEnv(attr1: string, attr2: string): boolean { + return [cdk.TokenComparison.SAME, cdk.TokenComparison.BOTH_UNRESOLVED].includes(cdk.Token.compareStrings(attr1, attr2)); +} 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..681db3d5c2aaf --- /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: '234567890123', + }, + }); + + // 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 234567890123/); + }); + + 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: '234567890123', + }, + }); + + // 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 234567890123/); + }); + + 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: '234567890123', + }, + }); + 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: '234567890123', + }, + }); + + // 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 234567890123/); + }); + + 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 0000000000000..af6ae9c76e414 Binary files /dev/null and b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.tar.gz differ diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/Dockerfile b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/Dockerfile new file mode 100644 index 0000000000000..7eb03f499c9c6 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/Dockerfile @@ -0,0 +1,19 @@ +FROM --platform=linux/amd64 python:3 + +# The following label allows this image to deployed within an inference pipeline +LABEL com.amazonaws.sagemaker.capabilities.accept-bind-to-port=true + +# Avoid buffering output to expedite log delivery +ENV PYTHONUNBUFFERED=TRUE + +# Default to port 8080 unless SageMaker has passed in its own port (for use within an inference pipline) +ENV SAGEMAKER_BIND_TO_PORT=${SAGEMAKER_BIND_TO_PORT:-8080} +EXPOSE $SAGEMAKER_BIND_TO_PORT + +# Set up the server application +ENV PROGRAM_DIRECTORY=/opt/program +RUN mkdir -p $PROGRAM_DIRECTORY +COPY index.py $PROGRAM_DIRECTORY +ENV PATH="${PROGRAM_DIRECTORY}:${PATH}" +WORKDIR $PROGRAM_DIRECTORY +ENTRYPOINT ["python3", "index.py"] diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.html b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.html new file mode 100644 index 0000000000000..3b18e512dba79 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.html @@ -0,0 +1 @@ +hello world diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.py b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.py new file mode 100644 index 0000000000000..f5ab0458f1cb1 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a/index.py @@ -0,0 +1,48 @@ +#!/usr/bin/python + +""" +This script stands up a lightweight HTTP server listening at the port specified in the +SAGEMAKER_BIND_TO_PORT environment variable. It loads an optional artifact from the path +/opt/ml/model/artifact.txt and returns information about the artifact in response to every +invocation request. Ping requests will always succeed. +""" + +import http.server +import os +import socketserver + +class SimpleSageMakerServer(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + if self.path == '/ping': + self.respond(200, 'Healthy') + else: + self.respond(404, 'Not Found') + + def do_POST(self): + if self.path == '/invocations': + self.respond(200, 'Artifact info: {}'.format(ARTIFACT)) + else: + self.respond(404, 'Not Found') + + def respond(self, status, response): + self.send_response(status) + self.send_header('Content-type', 'text/plain') + self.end_headers() + self.wfile.write(bytes('{}\n'.format(response), 'utf-8')) + + +PORT = int(os.environ['SAGEMAKER_BIND_TO_PORT']) +ARTIFACT_PATH = '/opt/ml/model/artifact.txt' + +print('Looking for model artifacts') +if (os.path.isfile(ARTIFACT_PATH)): + print('Loading model artifact from {}'.format(ARTIFACT_PATH)) + with open(ARTIFACT_PATH, 'r') as artifact_file: + ARTIFACT = artifact_file.read().splitlines() +else: + print('No model artifact present at {}'.format(ARTIFACT_PATH)) + ARTIFACT = 'No artifacts are present' + +with socketserver.TCPServer(('', PORT), SimpleSageMakerServer) as httpd: + print('Serving requests at port {}'.format(PORT)) + httpd.serve_forever() diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.assets.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.assets.json new file mode 100644 index 0000000000000..e9910a9e829fe --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.assets.json @@ -0,0 +1,45 @@ +{ + "version": "21.0.0", + "files": { + "126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916": { + "source": { + "path": "asset.126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.tar.gz", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "126d48fa0e32fbef5078b9d88658b35ad29d4291eb86675a64c75fa4f1338916.gz", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "16ab84c598574688eb95944b09c42d17b927d3f0a4725f9a9ccafcce2aefdf53": { + "source": { + "path": "aws-cdk-sagemaker-endpointconfig.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "16ab84c598574688eb95944b09c42d17b927d3f0a4725f9a9ccafcce2aefdf53.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": { + "442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a": { + "source": { + "directory": "asset.442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a" + }, + "destinations": { + "current_account-current_region": { + "repositoryName": "cdk-hnb659fds-container-assets-${AWS::AccountId}-${AWS::Region}", + "imageTag": "442a71de95281cb26bd41da567c79060206108b97bdde93cb4ce5f213f50013a", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-image-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.template.json b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.template.json new file mode 100644 index 0000000000000..f4258769bc2b2 --- /dev/null +++ b/packages/@aws-cdk/aws-sagemaker/test/integ.endpoint-config.js.snapshot/aws-cdk-sagemaker-endpointconfig.template.json @@ -0,0 +1,756 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "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" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet1RouteTableAssociation0B0896DC" + ] + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "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" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet2DefaultRouteB7481BBA", + "VPCPublicSubnet2RouteTableAssociation5A808732" + ] + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "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" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "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" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-sagemaker-endpointconfig/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "ModelWithArtifactAndVpcSecurityGroupB499C626": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "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" + } + } + }, + "ModelWithArtifactAndVpcRole6BA49FD3": { + "Type": "AWS::IAM::Role", + "Properties": { + "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" + ] + ] + } + ] + } + }, + "ModelWithArtifactAndVpcRoleDefaultPolicyC77E2AFA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "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" + } + ] + } + }, + "ModelWithArtifactAndVpcModel30604F15": { + "Type": "AWS::SageMaker::Model", + "Properties": { + "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": { + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "ModelWithArtifactAndVpcSecurityGroupB499C626", + "GroupId" + ] + } + ], + "Subnets": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "DependsOn": [ + "ModelWithArtifactAndVpcRoleDefaultPolicyC77E2AFA", + "ModelWithArtifactAndVpcRole6BA49FD3" + ] + }, + "ModelWithoutArtifactAndVpcRole10D89F15": { + "Type": "AWS::IAM::Role", + "Properties": { + "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" + ] + ] + } + ] + } + }, + "ModelWithoutArtifactAndVpcRoleDefaultPolicy88BAF094": { + "Type": "AWS::IAM::Policy", + "Properties": { + "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" + } + ] + } + }, + "ModelWithoutArtifactAndVpcModel9A8AD144": { + "Type": "AWS::SageMaker::Model", + "Properties": { + "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" + } + } + }, + "DependsOn": [ + "ModelWithoutArtifactAndVpcRoleDefaultPolicy88BAF094", + "ModelWithoutArtifactAndVpcRole10D89F15" + ] + }, + "EndpointConfigFD7B6F91": { + "Type": "AWS::SageMaker::EndpointConfig", + "Properties": { + "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" + } + ] + } + } + }, + "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/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], +});