Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 11 additions & 47 deletions src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ async function main() {
memoryName?: string;
containerUri?: string;
hasDockerfile?: boolean;
dockerfileName?: string;
harnessDir?: string;
dockerfile?: string;
codeLocation?: string;
tools?: { type: string; name: string }[];
apiKeyArn?: string;
}[] = [];
Expand All @@ -126,8 +126,8 @@ async function main() {
memoryName: harnessSpec.memory?.name,
containerUri: harnessSpec.containerUri,
hasDockerfile: !!harnessSpec.dockerfile,
dockerfileName: harnessSpec.dockerfile,
harnessDir,
dockerfile: harnessSpec.dockerfile,
codeLocation: harnessSpec.dockerfile ? harnessDir : undefined,
tools: harnessSpec.tools,
apiKeyArn: harnessSpec.model?.apiKeyArn,
});
Expand Down Expand Up @@ -301,10 +301,6 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/lib/cdk-stack.ts shou
AgentCoreMcp,
type AgentCoreProjectSpec,
type AgentCoreMcpSpec,
ContainerSourceAssetFromPath,
AgentEcrRepository,
ContainerBuildProject,
ContainerImageBuilder,
} from '@aws/agentcore-cdk';
import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
Expand All @@ -315,8 +311,8 @@ export interface HarnessConfig {
memoryName?: string;
containerUri?: string;
hasDockerfile?: boolean;
dockerfileName?: string;
harnessDir?: string;
dockerfile?: string;
codeLocation?: string;
tools?: { type: string; name: string }[];
apiKeyArn?: string;
}
Expand All @@ -336,6 +332,10 @@ export interface AgentCoreStackProps extends StackProps {
credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
/**
* Harness role configurations. Each entry creates an IAM execution role for a harness.
*
* When \`hasDockerfile\` is true and \`codeLocation\` is provided (without an explicit
* \`containerUri\`), the L3 construct builds and pushes a container image via CodeBuild
* and emits its URI as a stack output for the post-CDK harness deployer.
*/
harnesses?: HarnessConfig[];
}
Expand All @@ -355,46 +355,10 @@ export class AgentCoreStack extends Stack {

const { spec, mcpSpec, credentials, harnesses } = props;

// Build container images for harnesses that specify a dockerfile (no containerUri).
// Produces CDK outputs consumed by the imperative harness deployer.
const harnessesForCdk = harnesses ? [...harnesses] : [];
if (harnesses) {
for (let i = 0; i < harnesses.length; i++) {
const h = harnesses[i]!;
if (h.hasDockerfile && !h.containerUri && h.harnessDir) {
const pascalName = h.name.replace(/(^|_)([a-z])/g, (_: string, __: string, c: string) => c.toUpperCase());
const sourceAsset = new ContainerSourceAssetFromPath(this, \`Harness\${pascalName}SourceAsset\`, {
sourcePath: h.harnessDir,
});
const ecrRepo = new AgentEcrRepository(this, \`Harness\${pascalName}EcrRepo\`, {
projectName: spec.name,
agentName: \`harness-\${h.name}\`,
});
const buildProject = ContainerBuildProject.getOrCreate(this);
buildProject.grantPushTo(ecrRepo.repository);
sourceAsset.asset.grantRead(buildProject.role);

const builder = new ContainerImageBuilder(this, \`Harness\${pascalName}ContainerBuild\`, {
buildProject,
sourceAsset,
repository: ecrRepo,
dockerfile: h.dockerfileName ?? 'Dockerfile',
});

new CfnOutput(this, \`Harness\${pascalName}ContainerUriOutput\`, {
value: builder.containerUri,
});

// Pass the built containerUri to the harness role construct so it gets ECR pull permissions
harnessesForCdk[i] = { ...h, containerUri: builder.containerUri };
}
}
}

// Create AgentCoreApplication with all agents and harness roles
this.application = new AgentCoreApplication(this, 'Application', {
spec,
harnesses: harnessesForCdk.length > 0 ? harnessesForCdk : undefined,
harnesses: harnesses?.length ? harnesses : undefined,
});

// Create AgentCoreMcp if there are gateways configured
Expand Down
8 changes: 4 additions & 4 deletions src/assets/cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ async function main() {
memoryName?: string;
containerUri?: string;
hasDockerfile?: boolean;
dockerfileName?: string;
harnessDir?: string;
dockerfile?: string;
codeLocation?: string;
tools?: { type: string; name: string }[];
apiKeyArn?: string;
}[] = [];
Expand All @@ -81,8 +81,8 @@ async function main() {
memoryName: harnessSpec.memory?.name,
containerUri: harnessSpec.containerUri,
hasDockerfile: !!harnessSpec.dockerfile,
dockerfileName: harnessSpec.dockerfile,
harnessDir,
dockerfile: harnessSpec.dockerfile,
codeLocation: harnessSpec.dockerfile ? harnessDir : undefined,
tools: harnessSpec.tools,
apiKeyArn: harnessSpec.model?.apiKeyArn,
});
Expand Down
50 changes: 7 additions & 43 deletions src/assets/cdk/lib/cdk-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import {
AgentCoreMcp,
type AgentCoreProjectSpec,
type AgentCoreMcpSpec,
ContainerSourceAssetFromPath,
AgentEcrRepository,
ContainerBuildProject,
ContainerImageBuilder,
} from '@aws/agentcore-cdk';
import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
Expand All @@ -17,8 +13,8 @@ export interface HarnessConfig {
memoryName?: string;
containerUri?: string;
hasDockerfile?: boolean;
dockerfileName?: string;
harnessDir?: string;
dockerfile?: string;
codeLocation?: string;
tools?: { type: string; name: string }[];
apiKeyArn?: string;
}
Expand All @@ -38,6 +34,10 @@ export interface AgentCoreStackProps extends StackProps {
credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
/**
* Harness role configurations. Each entry creates an IAM execution role for a harness.
*
* When `hasDockerfile` is true and `codeLocation` is provided (without an explicit
* `containerUri`), the L3 construct builds and pushes a container image via CodeBuild
* and emits its URI as a stack output for the post-CDK harness deployer.
*/
harnesses?: HarnessConfig[];
}
Expand All @@ -57,46 +57,10 @@ export class AgentCoreStack extends Stack {

const { spec, mcpSpec, credentials, harnesses } = props;

// Build container images for harnesses that specify a dockerfile (no containerUri).
// Produces CDK outputs consumed by the imperative harness deployer.
const harnessesForCdk = harnesses ? [...harnesses] : [];
if (harnesses) {
for (let i = 0; i < harnesses.length; i++) {
const h = harnesses[i]!;
if (h.hasDockerfile && !h.containerUri && h.harnessDir) {
const pascalName = h.name.replace(/(^|_)([a-z])/g, (_: string, __: string, c: string) => c.toUpperCase());
const sourceAsset = new ContainerSourceAssetFromPath(this, `Harness${pascalName}SourceAsset`, {
sourcePath: h.harnessDir,
});
const ecrRepo = new AgentEcrRepository(this, `Harness${pascalName}EcrRepo`, {
projectName: spec.name,
agentName: `harness-${h.name}`,
});
const buildProject = ContainerBuildProject.getOrCreate(this);
buildProject.grantPushTo(ecrRepo.repository);
sourceAsset.asset.grantRead(buildProject.role);

const builder = new ContainerImageBuilder(this, `Harness${pascalName}ContainerBuild`, {
buildProject,
sourceAsset,
repository: ecrRepo,
dockerfile: h.dockerfileName ?? 'Dockerfile',
});

new CfnOutput(this, `Harness${pascalName}ContainerUriOutput`, {
value: builder.containerUri,
});

// Pass the built containerUri to the harness role construct so it gets ECR pull permissions
harnessesForCdk[i] = { ...h, containerUri: builder.containerUri };
}
}
}

// Create AgentCoreApplication with all agents and harness roles
this.application = new AgentCoreApplication(this, 'Application', {
spec,
harnesses: harnessesForCdk.length > 0 ? harnessesForCdk : undefined,
harnesses: harnesses?.length ? harnesses : undefined,
});

// Create AgentCoreMcp if there are gateways configured
Expand Down
Loading