Skip to content

Commit af43d43

Browse files
go-to-kmrgrain
andauthored
feat(cli): support hotswap for AWS::BedrockAgentCore::Runtime (#991)
Fixes #947 This PR adds hotswap support for `AWS::Bedrock::AgentRuntime`. This feature can detect both of S3 code files and ECR images changes to deploy with hotswap. ## Changes 1. Added hotswap support for Bedrock Agent Runtime resources 2. Added `@aws-sdk/client-bedrock-agentcore-control` dependency ~~3. Added yarn `resolutions` to prevent ESM/CommonJS incompatibility (see [the comment](#991 (comment)) for details)~~ ~~4. Modified `yarn-cling` to support yarn resolutions field (see [the comment](#991 (comment)) for rationale)~~ ~~**Note on 3 and 4**: I chose this approach to resolve build failures after adding the Bedrock SDK, though I'm not certain it's the optimal solution. If there's a better approach, I'd welcome your feedback. See the [commit "3904aab4"](3904aab) for details. FYI, I deleted the yarn.lock file and did a clean install again instead of resolutions, but I got the same error.~~ Tried to revert the commit and modify the lock file manually, build succeeded. To be specific, after installing `@aws-sdk/client-bedrock-agentcore-control`, this [change](e747f2c) was automatically made. I then manually reverted [everything](a6f2daf) except `"@aws-sdk/client-bedrock-agentcore-control@^3":` (since it was not directly related to that SDK). The build then succeeded. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license --------- Co-authored-by: Momo Kornher <kornherm@amazon.co.uk>
1 parent 92af268 commit af43d43

File tree

18 files changed

+1546
-1
lines changed

18 files changed

+1546
-1
lines changed

.projenrc.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ const toolkitLib = configureProject(
808808
cdkAssetsLib.customizeReference({ versionType: 'any-minor' }), // stay within the same MV, otherwise any should work
809809
`${cxApi}@^2`, // stay within the same MV, otherwise any should work
810810
sdkDepForLib('@aws-sdk/client-appsync'),
811+
sdkDepForLib('@aws-sdk/client-bedrock-agentcore-control'),
811812
sdkDepForLib('@aws-sdk/client-cloudformation'),
812813
sdkDepForLib('@aws-sdk/client-cloudwatch-logs'),
813814
sdkDepForLib('@aws-sdk/client-cloudcontrol'),
@@ -1128,6 +1129,7 @@ const cli = configureProject(
11281129
toolkitLib,
11291130
'archiver',
11301131
'@aws-sdk/client-appsync',
1132+
'@aws-sdk/client-bedrock-agentcore-control',
11311133
'@aws-sdk/client-cloudformation',
11321134
'@aws-sdk/client-cloudwatch-logs',
11331135
'@aws-sdk/client-cloudcontrol',

packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') {
3232
aws_lambda_nodejs: node_lambda,
3333
aws_ecr_assets: docker,
3434
aws_appsync: appsync,
35+
aws_bedrockagentcore: bedrockagentcore,
3536
Stack
3637
} = require('aws-cdk-lib');
3738
}
@@ -672,6 +673,44 @@ class EcsHotswapStack extends cdk.Stack {
672673
}
673674
}
674675

676+
class AgentCoreHotswapStack extends cdk.Stack {
677+
constructor(parent, id, props) {
678+
super(parent, id, props);
679+
680+
const role = new iam.Role(this, 'RuntimeRole', {
681+
assumedBy: new iam.ServicePrincipal('bedrock-agentcore.amazonaws.com'),
682+
managedPolicies: [
683+
iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonBedrockFullAccess'),
684+
],
685+
});
686+
687+
const image = new docker.DockerImageAsset(this, 'Image', {
688+
directory: path.join(__dirname, 'docker'),
689+
platform: docker.Platform.LINUX_ARM64,
690+
});
691+
image.repository.grantPull(role);
692+
693+
const runtime = new bedrockagentcore.CfnRuntime(this, 'Runtime', {
694+
agentRuntimeName: 'test_runtime',
695+
roleArn: role.roleArn,
696+
networkConfiguration: {
697+
networkMode: 'PUBLIC',
698+
},
699+
agentRuntimeArtifact: {
700+
containerConfiguration: {
701+
containerUri: image.imageUri,
702+
},
703+
},
704+
description: process.env.DYNAMIC_BEDROCK_RUNTIME_DESCRIPTION ?? 'runtime',
705+
environmentVariables: {
706+
TEST_VAR: process.env.DYNAMIC_BEDROCK_RUNTIME_ENV_VAR ?? 'original',
707+
},
708+
});
709+
runtime.node.addDependency(role);
710+
new cdk.CfnOutput(this, 'RuntimeId', { value: runtime.ref });
711+
}
712+
}
713+
675714
class DockerStack extends cdk.Stack {
676715
constructor(parent, id, props) {
677716
super(parent, id, props);
@@ -928,6 +967,7 @@ switch (stackSet) {
928967
new SessionTagsWithNoExecutionRoleCustomSynthesizerStack(app, `${stackPrefix}-session-tags-with-custom-synthesizer`);
929968
new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`);
930969
new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`);
970+
new AgentCoreHotswapStack(app, `${stackPrefix}-agentcore-hotswap`);
931971
new AppSyncHotswapStack(app, `${stackPrefix}-appsync-hotswap`);
932972
new DockerStack(app, `${stackPrefix}-docker`);
933973
new DockerInUseStack(app, `${stackPrefix}-docker-in-use`);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
2+
import { integTest, withDefaultFixture } from '../../../lib';
3+
4+
jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
5+
6+
// Bedrock AgentCore Runtime is only available in specific regions
7+
// Source: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html
8+
const SUPPORTED_REGIONS = [
9+
'ap-south-1',
10+
'ap-southeast-1',
11+
'ap-southeast-2',
12+
'ap-northeast-1',
13+
'eu-west-1',
14+
'eu-central-1',
15+
'us-east-1',
16+
'us-east-2',
17+
'us-west-2',
18+
];
19+
20+
integTest(
21+
'hotswap deployment supports Bedrock AgentCore Runtime',
22+
withDefaultFixture(async (fixture) => {
23+
const currentRegion = fixture.aws.region;
24+
if (!SUPPORTED_REGIONS.includes(currentRegion)) {
25+
fixture.log(`Skipping test: Bedrock AgentCore Runtime is not supported in region ${currentRegion}`);
26+
return;
27+
}
28+
29+
// GIVEN
30+
const stackArn = await fixture.cdkDeploy('agentcore-hotswap', {
31+
captureStderr: false,
32+
modEnv: {
33+
DYNAMIC_BEDROCK_RUNTIME_DESCRIPTION: 'original description',
34+
DYNAMIC_BEDROCK_RUNTIME_ENV_VAR: 'original value',
35+
},
36+
});
37+
38+
// WHEN
39+
const deployOutput = await fixture.cdkDeploy('agentcore-hotswap', {
40+
options: ['--hotswap'],
41+
captureStderr: true,
42+
onlyStderr: true,
43+
modEnv: {
44+
DYNAMIC_BEDROCK_RUNTIME_DESCRIPTION: 'new description',
45+
DYNAMIC_BEDROCK_RUNTIME_ENV_VAR: 'new value',
46+
},
47+
});
48+
49+
// Extract stack name from ARN (format: arn:aws:cloudformation:region:account:stack/name/id)
50+
// Using the full ARN can cause "Stack name cannot exceed 128 characters" error in some cases
51+
const stackName = stackArn.split('/')[1];
52+
53+
const response = await fixture.aws.cloudFormation.send(
54+
new DescribeStacksCommand({
55+
StackName: stackName,
56+
}),
57+
);
58+
const runtimeId = response.Stacks?.[0].Outputs?.find((output) => output.OutputKey === 'RuntimeId')?.OutputValue;
59+
60+
// THEN
61+
62+
// The deployment should not trigger a full deployment, thus the stack's status must remains
63+
// "CREATE_COMPLETE"
64+
expect(response.Stacks?.[0].StackStatus).toEqual('CREATE_COMPLETE');
65+
// The entire string fails locally due to formatting. Making this test less specific
66+
expect(deployOutput).toMatch(/hotswapped!/);
67+
expect(deployOutput).toContain(runtimeId);
68+
}),
69+
);

packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,212 @@ The @aws-cdk/integ-runner package includes the following third-party software/li
822822
limitations under the License.
823823

824824

825+
----------------
826+
827+
** @aws-sdk/client-bedrock-agentcore-control@3.953.0 - https://www.npmjs.com/package/@aws-sdk/client-bedrock-agentcore-control/v/3.953.0 | Apache-2.0
828+
Apache License
829+
Version 2.0, January 2004
830+
http://www.apache.org/licenses/
831+
832+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
833+
834+
1. Definitions.
835+
836+
"License" shall mean the terms and conditions for use, reproduction,
837+
and distribution as defined by Sections 1 through 9 of this document.
838+
839+
"Licensor" shall mean the copyright owner or entity authorized by
840+
the copyright owner that is granting the License.
841+
842+
"Legal Entity" shall mean the union of the acting entity and all
843+
other entities that control, are controlled by, or are under common
844+
control with that entity. For the purposes of this definition,
845+
"control" means (i) the power, direct or indirect, to cause the
846+
direction or management of such entity, whether by contract or
847+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
848+
outstanding shares, or (iii) beneficial ownership of such entity.
849+
850+
"You" (or "Your") shall mean an individual or Legal Entity
851+
exercising permissions granted by this License.
852+
853+
"Source" form shall mean the preferred form for making modifications,
854+
including but not limited to software source code, documentation
855+
source, and configuration files.
856+
857+
"Object" form shall mean any form resulting from mechanical
858+
transformation or translation of a Source form, including but
859+
not limited to compiled object code, generated documentation,
860+
and conversions to other media types.
861+
862+
"Work" shall mean the work of authorship, whether in Source or
863+
Object form, made available under the License, as indicated by a
864+
copyright notice that is included in or attached to the work
865+
(an example is provided in the Appendix below).
866+
867+
"Derivative Works" shall mean any work, whether in Source or Object
868+
form, that is based on (or derived from) the Work and for which the
869+
editorial revisions, annotations, elaborations, or other modifications
870+
represent, as a whole, an original work of authorship. For the purposes
871+
of this License, Derivative Works shall not include works that remain
872+
separable from, or merely link (or bind by name) to the interfaces of,
873+
the Work and Derivative Works thereof.
874+
875+
"Contribution" shall mean any work of authorship, including
876+
the original version of the Work and any modifications or additions
877+
to that Work or Derivative Works thereof, that is intentionally
878+
submitted to Licensor for inclusion in the Work by the copyright owner
879+
or by an individual or Legal Entity authorized to submit on behalf of
880+
the copyright owner. For the purposes of this definition, "submitted"
881+
means any form of electronic, verbal, or written communication sent
882+
to the Licensor or its representatives, including but not limited to
883+
communication on electronic mailing lists, source code control systems,
884+
and issue tracking systems that are managed by, or on behalf of, the
885+
Licensor for the purpose of discussing and improving the Work, but
886+
excluding communication that is conspicuously marked or otherwise
887+
designated in writing by the copyright owner as "Not a Contribution."
888+
889+
"Contributor" shall mean Licensor and any individual or Legal Entity
890+
on behalf of whom a Contribution has been received by Licensor and
891+
subsequently incorporated within the Work.
892+
893+
2. Grant of Copyright License. Subject to the terms and conditions of
894+
this License, each Contributor hereby grants to You a perpetual,
895+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
896+
copyright license to reproduce, prepare Derivative Works of,
897+
publicly display, publicly perform, sublicense, and distribute the
898+
Work and such Derivative Works in Source or Object form.
899+
900+
3. Grant of Patent License. Subject to the terms and conditions of
901+
this License, each Contributor hereby grants to You a perpetual,
902+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
903+
(except as stated in this section) patent license to make, have made,
904+
use, offer to sell, sell, import, and otherwise transfer the Work,
905+
where such license applies only to those patent claims licensable
906+
by such Contributor that are necessarily infringed by their
907+
Contribution(s) alone or by combination of their Contribution(s)
908+
with the Work to which such Contribution(s) was submitted. If You
909+
institute patent litigation against any entity (including a
910+
cross-claim or counterclaim in a lawsuit) alleging that the Work
911+
or a Contribution incorporated within the Work constitutes direct
912+
or contributory patent infringement, then any patent licenses
913+
granted to You under this License for that Work shall terminate
914+
as of the date such litigation is filed.
915+
916+
4. Redistribution. You may reproduce and distribute copies of the
917+
Work or Derivative Works thereof in any medium, with or without
918+
modifications, and in Source or Object form, provided that You
919+
meet the following conditions:
920+
921+
(a) You must give any other recipients of the Work or
922+
Derivative Works a copy of this License; and
923+
924+
(b) You must cause any modified files to carry prominent notices
925+
stating that You changed the files; and
926+
927+
(c) You must retain, in the Source form of any Derivative Works
928+
that You distribute, all copyright, patent, trademark, and
929+
attribution notices from the Source form of the Work,
930+
excluding those notices that do not pertain to any part of
931+
the Derivative Works; and
932+
933+
(d) If the Work includes a "NOTICE" text file as part of its
934+
distribution, then any Derivative Works that You distribute must
935+
include a readable copy of the attribution notices contained
936+
within such NOTICE file, excluding those notices that do not
937+
pertain to any part of the Derivative Works, in at least one
938+
of the following places: within a NOTICE text file distributed
939+
as part of the Derivative Works; within the Source form or
940+
documentation, if provided along with the Derivative Works; or,
941+
within a display generated by the Derivative Works, if and
942+
wherever such third-party notices normally appear. The contents
943+
of the NOTICE file are for informational purposes only and
944+
do not modify the License. You may add Your own attribution
945+
notices within Derivative Works that You distribute, alongside
946+
or as an addendum to the NOTICE text from the Work, provided
947+
that such additional attribution notices cannot be construed
948+
as modifying the License.
949+
950+
You may add Your own copyright statement to Your modifications and
951+
may provide additional or different license terms and conditions
952+
for use, reproduction, or distribution of Your modifications, or
953+
for any such Derivative Works as a whole, provided Your use,
954+
reproduction, and distribution of the Work otherwise complies with
955+
the conditions stated in this License.
956+
957+
5. Submission of Contributions. Unless You explicitly state otherwise,
958+
any Contribution intentionally submitted for inclusion in the Work
959+
by You to the Licensor shall be under the terms and conditions of
960+
this License, without any additional terms or conditions.
961+
Notwithstanding the above, nothing herein shall supersede or modify
962+
the terms of any separate license agreement you may have executed
963+
with Licensor regarding such Contributions.
964+
965+
6. Trademarks. This License does not grant permission to use the trade
966+
names, trademarks, service marks, or product names of the Licensor,
967+
except as required for reasonable and customary use in describing the
968+
origin of the Work and reproducing the content of the NOTICE file.
969+
970+
7. Disclaimer of Warranty. Unless required by applicable law or
971+
agreed to in writing, Licensor provides the Work (and each
972+
Contributor provides its Contributions) on an "AS IS" BASIS,
973+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
974+
implied, including, without limitation, any warranties or conditions
975+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
976+
PARTICULAR PURPOSE. You are solely responsible for determining the
977+
appropriateness of using or redistributing the Work and assume any
978+
risks associated with Your exercise of permissions under this License.
979+
980+
8. Limitation of Liability. In no event and under no legal theory,
981+
whether in tort (including negligence), contract, or otherwise,
982+
unless required by applicable law (such as deliberate and grossly
983+
negligent acts) or agreed to in writing, shall any Contributor be
984+
liable to You for damages, including any direct, indirect, special,
985+
incidental, or consequential damages of any character arising as a
986+
result of this License or out of the use or inability to use the
987+
Work (including but not limited to damages for loss of goodwill,
988+
work stoppage, computer failure or malfunction, or any and all
989+
other commercial damages or losses), even if such Contributor
990+
has been advised of the possibility of such damages.
991+
992+
9. Accepting Warranty or Additional Liability. While redistributing
993+
the Work or Derivative Works thereof, You may choose to offer,
994+
and charge a fee for, acceptance of support, warranty, indemnity,
995+
or other liability obligations and/or rights consistent with this
996+
License. However, in accepting such obligations, You may act only
997+
on Your own behalf and on Your sole responsibility, not on behalf
998+
of any other Contributor, and only if You agree to indemnify,
999+
defend, and hold each Contributor harmless for any liability
1000+
incurred by, or claims asserted against, such Contributor by reason
1001+
of your accepting any such warranty or additional liability.
1002+
1003+
END OF TERMS AND CONDITIONS
1004+
1005+
APPENDIX: How to apply the Apache License to your work.
1006+
1007+
To apply the Apache License to your work, attach the following
1008+
boilerplate notice, with the fields enclosed by brackets "{}"
1009+
replaced with your own identifying information. (Don't include
1010+
the brackets!) The text should be enclosed in the appropriate
1011+
comment syntax for the file format. We also recommend that a
1012+
file or class name and description of purpose be included on the
1013+
same "printed page" as the copyright notice for easier
1014+
identification within third-party archives.
1015+
1016+
Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
1017+
1018+
Licensed under the Apache License, Version 2.0 (the "License");
1019+
you may not use this file except in compliance with the License.
1020+
You may obtain a copy of the License at
1021+
1022+
http://www.apache.org/licenses/LICENSE-2.0
1023+
1024+
Unless required by applicable law or agreed to in writing, software
1025+
distributed under the License is distributed on an "AS IS" BASIS,
1026+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1027+
See the License for the specific language governing permissions and
1028+
limitations under the License.
1029+
1030+
8251031
----------------
8261032

8271033
** @aws-sdk/client-cloudcontrol@3.953.0 - https://www.npmjs.com/package/@aws-sdk/client-cloudcontrol/v/3.953.0 | Apache-2.0

packages/@aws-cdk/toolkit-lib/.projen/deps.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)