Skip to content

Commit 83ae514

Browse files
committed
fix: resolve e2e import test concurrency races
Fix two independent concurrency issues causing flaky e2e import tests: 1. TOCTOU race in evaluator import (import-evaluator.ts): The beforeConfigWrite hook lists all online eval configs then fetches details for each with Promise.all. If a config is deleted between the list and get calls, the API throws 'Online evaluation configuration not found' and the entire import fails. Fixed by using Promise.allSettled and filtering out disappeared configs. 2. Resource name collisions across parallel CI shards (setup_*.py): Python setup scripts generated resource names using int(time.time()) (second-level precision). Parallel CI shards starting in the same second would collide with ConflictException. The test already passes a unique RESOURCE_SUFFIX env var but scripts ignored it for naming. Added NAME_SUFFIX to common.py that prefers RESOURCE_SUFFIX when set, and updated all setup scripts to use it.
1 parent a4c37a2 commit 83ae514

6 files changed

Lines changed: 19 additions & 11 deletions

File tree

e2e-tests/fixtures/import/common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
import json
33
import os
44
import time
5+
import uuid
56
import zipfile
67
import tempfile
78

89
import boto3
910

1011
REGION = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") or "us-east-1"
1112
RESOURCE_SUFFIX = os.environ.get("RESOURCE_SUFFIX", "")
13+
# Unique suffix for resource names — avoids collisions across parallel CI shards.
14+
NAME_SUFFIX = RESOURCE_SUFFIX or uuid.uuid4().hex[:12]
1215
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
1316
APP_DIR = os.path.join(SCRIPT_DIR, "app")
1417
_resources_name = f"bugbash-resources-{RESOURCE_SUFFIX}.json" if RESOURCE_SUFFIX else "bugbash-resources.json"

e2e-tests/fixtures/import/setup_evaluator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
from common import (
1313
get_control_client, save_resource, tag_resource,
1414
wait_for_evaluator, print_import_command,
15+
NAME_SUFFIX,
1516
)
1617

1718
DEFAULT_EVALUATOR_MODEL = os.environ.get("DEFAULT_EVALUATOR_MODEL", "us.anthropic.claude-sonnet-4-5-20250929-v1:0")
1819

1920

2021
def main():
2122
client = get_control_client()
22-
ts = int(time.time())
23-
evaluator_name = f"bugbash_eval_{ts}"
23+
evaluator_name = f"bugbash_eval_{NAME_SUFFIX}"
2424

2525
print(f"Creating evaluator: {evaluator_name}")
2626
resp = client.create_evaluator(

e2e-tests/fixtures/import/setup_gateway.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
from common import (
1717
REGION, get_control_client, ensure_role, save_resource,
1818
tag_resource, wait_for_gateway, wait_for_gateway_target,
19+
NAME_SUFFIX,
1920
)
2021

2122

2223
def main():
2324
role_arn = ensure_role()
2425
client = get_control_client()
25-
ts = int(time.time())
26-
gateway_name = f"bugbashGw{ts}"
26+
gateway_name = f"bugbashGw{NAME_SUFFIX}"
2727

2828
# ------------------------------------------------------------------
2929
# 1. Create gateway

e2e-tests/fixtures/import/setup_memory_full.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@
1212
from common import (
1313
ensure_role, get_control_client, wait_for_memory,
1414
save_resource, print_import_command, tag_resource,
15+
NAME_SUFFIX,
1516
)
1617

1718

1819
def main():
1920
role_arn = ensure_role()
2021
client = get_control_client()
21-
memory_name = f"bugbash_memory_{int(time.time())}"
22+
memory_name = f"bugbash_memory_{NAME_SUFFIX}"
2223

2324
print(f"Creating memory: {memory_name}")
2425
resp = client.create_memory(
2526
name=memory_name,
26-
clientToken=f"bugbash-{int(time.time())}",
27+
clientToken=f"bugbash-{NAME_SUFFIX}",
2728
eventExpiryDuration=30,
2829
memoryExecutionRoleArn=role_arn,
2930
memoryStrategies=[

e2e-tests/fixtures/import/setup_runtime_basic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
from common import (
1212
ensure_role, get_control_client, wait_for_runtime,
1313
save_resource, print_import_command, upload_code,
14+
NAME_SUFFIX,
1415
)
1516

1617

1718
def main():
1819
role_arn = ensure_role()
1920
client = get_control_client()
20-
ts = int(time.time())
21-
runtime_name = f"bugbash_basic_{ts}"
21+
runtime_name = f"bugbash_basic_{NAME_SUFFIX}"
2222

23-
bucket, s3_key = upload_code(f"bugbash-basic-{ts}")
23+
bucket, s3_key = upload_code(f"bugbash-basic-{NAME_SUFFIX}")
2424

2525
print(f"Creating basic runtime: {runtime_name}")
2626
resp = client.create_agent_runtime(

src/cli/commands/import/import-evaluator.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Evaluator } from '../../../schema';
2-
import type { EvaluatorSummary, GetEvaluatorResult } from '../../aws/agentcore-control';
2+
import type { EvaluatorSummary, GetEvaluatorResult, GetOnlineEvalConfigResult } from '../../aws/agentcore-control';
33
import {
44
getEvaluator,
55
getOnlineEvaluationConfig,
@@ -92,11 +92,15 @@ const evaluatorDescriptor: ResourceImportDescriptor<GetEvaluatorResult, Evaluato
9292

9393
const oecSummaries = await listAllOnlineEvaluationConfigs({ region: target.region });
9494
if (oecSummaries.length > 0) {
95-
const oecDetails = await Promise.all(
95+
// Use allSettled to tolerate configs deleted between list and get (TOCTOU race).
96+
const settled = await Promise.allSettled(
9697
oecSummaries.map(s =>
9798
getOnlineEvaluationConfig({ region: target.region, configId: s.onlineEvaluationConfigId })
9899
)
99100
);
101+
const oecDetails = settled
102+
.filter((r): r is PromiseFulfilledResult<GetOnlineEvalConfigResult> => r.status === 'fulfilled')
103+
.map(r => r.value);
100104

101105
const referencingOec = oecDetails.find(oec => oec.evaluatorIds?.includes(detail.evaluatorId));
102106

0 commit comments

Comments
 (0)