Skip to content
Open
26 changes: 26 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/actions/diagnose/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,30 @@ export interface TracedResourceError {
* (Not optional on purpose so we are not allowed to forget to call the code that should fill it)
*/
readonly sourceTrace: SourceTrace | undefined;

/**
* Additional context gathered from AWS service APIs to help diagnose the root cause.
*
* For example, CloudWatch Logs from an ECS service whose tasks failed to start.
*/
readonly additionalContext?: AdditionalDiagnosticContext[];
}

export interface AdditionalDiagnosticContext {
/**
* A short description of where this context came from
*
* @example "CloudWatch Logs (log-group-name)"
*/
readonly source: string;

/**
* The log lines or messages retrieved
*/
readonly messages: string[];

/**
* An optional console deep link for further investigation
*/
readonly link?: string;
}
17 changes: 17 additions & 0 deletions packages/@aws-cdk/toolkit-lib/lib/api/aws-auth/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ import {
} from '@aws-sdk/client-ecr';
import type {
DescribeServicesCommandInput,
DescribeServicesCommandOutput,
DescribeTaskDefinitionCommandInput,
DescribeTaskDefinitionCommandOutput,
DescribeTasksCommandInput,
DescribeTasksCommandOutput,
RegisterTaskDefinitionCommandInput,
ListClustersCommandInput,
ListClustersCommandOutput,
Expand All @@ -261,6 +266,9 @@ import type {
UpdateServiceCommandOutput,
} from '@aws-sdk/client-ecs';
import {
DescribeServicesCommand,
DescribeTaskDefinitionCommand,
DescribeTasksCommand,
ECSClient,
ListClustersCommand,
RegisterTaskDefinitionCommand,
Expand Down Expand Up @@ -555,6 +563,9 @@ export interface IECRClient {
}

export interface IECSClient {
describeServices(input: DescribeServicesCommandInput): Promise<DescribeServicesCommandOutput>;
describeTaskDefinition(input: DescribeTaskDefinitionCommandInput): Promise<DescribeTaskDefinitionCommandOutput>;
describeTasks(input: DescribeTasksCommandInput): Promise<DescribeTasksCommandOutput>;
listClusters(input: ListClustersCommandInput): Promise<ListClustersCommandOutput>;
registerTaskDefinition(input: RegisterTaskDefinitionCommandInput): Promise<RegisterTaskDefinitionCommandOutput>;
updateService(input: UpdateServiceCommandInput): Promise<UpdateServiceCommandOutput>;
Expand Down Expand Up @@ -950,6 +961,12 @@ export class SDK {
public ecs(): IECSClient {
const client = new ECSClient(this.config);
return {
describeServices: (input: DescribeServicesCommandInput): Promise<DescribeServicesCommandOutput> =>
client.send(new DescribeServicesCommand(input)),
describeTaskDefinition: (input: DescribeTaskDefinitionCommandInput): Promise<DescribeTaskDefinitionCommandOutput> =>
client.send(new DescribeTaskDefinitionCommand(input)),
describeTasks: (input: DescribeTasksCommandInput): Promise<DescribeTasksCommandOutput> =>
client.send(new DescribeTasksCommand(input)),
listClusters: (input: ListClustersCommandInput): Promise<ListClustersCommandOutput> =>
client.send(new ListClustersCommand(input)),
registerTaskDefinition: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ export class Deployments {
sourceTracer: new StackArtifactSourceTracer(options.stack),
ioHelper: this.ioHelper,
topLevelStackHierarchicalId: options.stack.hierarchicalId,
additionalExplorationSdkProvider: async () => (await this.envs.accessStackForLookupBestEffort(options.stack)).sdk,
}),
}, this.ioHelper);
}
Expand Down Expand Up @@ -498,6 +499,7 @@ export class Deployments {
sourceTracer: new StackArtifactSourceTracer(stack),
ioHelper: this.ioHelper,
topLevelStackHierarchicalId: stack.hierarchicalId,
additionalExplorationSdkProvider: async () => (await this.envs.accessStackForLookupBestEffort(stack)).sdk,
}),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ function formatResourceErrors(es: TracedResourceError[]) {
nodeText.footer = e.sourceTrace?.creationStackTrace
? sideBySide(['Source Location:'], ' ', e.sourceTrace?.creationStackTrace)
: [];

if (e.additionalContext) {
for (const ctx of e.additionalContext) {
const ctxNode = b.nodeText(`${p}/${ctx.source.replace(/\//g, '|')}`);
ctxNode.header = [`📋 ${ctx.source}:`];
for (const msg of ctx.messages) {
ctxNode.body.push(msg);
}
if (ctx.link) {
ctxNode.body.push(`🔗 ${ctx.link}`);
}
}
}
}
return b.render();
}
Expand Down
Loading
Loading