Skip to content

Commit c0a3505

Browse files
committed
Add multi-architecture canary support (ARM64 Lambda)
Expand the smithy-rs canary to deploy and run ARM64 (aarch64) Lambda functions in addition to x86_64, catching architecture-specific runtime bugs like the crc-fast 1.4 SIGILL incident before merge. Changes: - Add --architecture flag to canary runner (run.rs), wire through to Lambda creation with .architectures() and arch-aware runtime selection (provided.al2 for x86_64, provided.al2023 for aarch64) - Add architecture-suffixed Lambda function names to prevent collisions when both canaries run in parallel - Accept architecture as 5th arg in run-canary script - Skip cross-compilation tool when building natively on ARM - Reduce hash truncation in bundle names (24->16 chars) to leave headroom for architecture suffix within Lambda 64-char name limit - Add canary-arm CI job that runs directly on ubuntu-24.04-arm without Docker (the existing Docker build image is x86_64-only in ECR) - Existing x86_64 canary is unchanged Supersedes #4428. Addresses #4380.
1 parent d6f5680 commit c0a3505

5 files changed

Lines changed: 139 additions & 17 deletions

File tree

.github/workflows/ci.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,52 @@ jobs:
371371
uses: ./smithy-rs/.github/actions/docker-build
372372
with:
373373
action: run-canary
374-
action-arguments: ${{ secrets.CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME }} ${{ steps.creds.outputs.aws-access-key-id }} ${{ steps.creds.outputs.aws-secret-access-key }} ${{ steps.creds.outputs.aws-session-token }}
374+
action-arguments: ${{ secrets.CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME }} ${{ steps.creds.outputs.aws-access-key-id }} ${{ steps.creds.outputs.aws-secret-access-key }} ${{ steps.creds.outputs.aws-session-token }} x86_64
375+
376+
canary-arm:
377+
name: Canary (aarch64)
378+
if: ${{ inputs.run_canary }}
379+
needs: generate
380+
runs-on: ubuntu-24.04-arm
381+
timeout-minutes: 30
382+
permissions:
383+
id-token: write
384+
contents: read
385+
steps:
386+
- uses: actions/checkout@v4
387+
with:
388+
path: smithy-rs
389+
ref: ${{ inputs.git_ref }}
390+
- name: Download artifacts
391+
uses: actions/download-artifact@v4
392+
with:
393+
name: artifacts-generate-aws-sdk
394+
path: artifacts-generate-aws-sdk
395+
- name: Extract artifacts
396+
run: tar xfz artifacts-generate-aws-sdk/artifacts-generate-aws-sdk.tar.gz
397+
- name: Install Rust
398+
uses: dtolnay/rust-toolchain@stable
399+
with:
400+
targets: aarch64-unknown-linux-musl
401+
- name: Install musl-tools
402+
run: sudo apt-get update && sudo apt-get install -y musl-tools
403+
- name: Configure credentials
404+
id: creds
405+
uses: aws-actions/configure-aws-credentials@v4
406+
with:
407+
role-to-assume: ${{ secrets.CANARY_GITHUB_ACTIONS_ROLE_ARN }}
408+
role-session-name: GitHubActions
409+
aws-region: us-west-2
410+
output-credentials: true
411+
- name: Run canary
412+
run: |
413+
export RUST_STABLE_VERSION="$(rustc --version | cut -d' ' -f2)"
414+
./smithy-rs/tools/ci-scripts/run-canary \
415+
${{ secrets.CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME }} \
416+
${{ steps.creds.outputs.aws-access-key-id }} \
417+
${{ steps.creds.outputs.aws-secret-access-key }} \
418+
${{ steps.creds.outputs.aws-session-token }} \
419+
aarch64
375420
376421
# This is always a failing job since forked repositories do not have necessary repository secrets
377422
# to run the PR bot workflow or the canary workflow

.github/workflows/manual-canary.yml

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,50 @@ jobs:
117117
uses: ./smithy-rs/.github/actions/docker-build
118118
with:
119119
action: run-canary
120-
action-arguments: ${{ secrets.CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME }} ${{ steps.creds.outputs.aws-access-key-id }} ${{ steps.creds.outputs.aws-secret-access-key }} ${{ steps.creds.outputs.aws-session-token }}
120+
action-arguments: ${{ secrets.CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME }} ${{ steps.creds.outputs.aws-access-key-id }} ${{ steps.creds.outputs.aws-secret-access-key }} ${{ steps.creds.outputs.aws-session-token }} x86_64
121+
122+
canary-arm:
123+
name: Canary (aarch64)
124+
needs:
125+
- generate
126+
- get-pr-info
127+
runs-on: ubuntu-24.04-arm
128+
timeout-minutes: 30
129+
permissions:
130+
id-token: write
131+
contents: read
132+
steps:
133+
- uses: actions/checkout@v4
134+
with:
135+
path: smithy-rs
136+
ref: ${{ inputs.commit_sha }}
137+
- name: Download artifacts
138+
uses: actions/download-artifact@v4
139+
with:
140+
name: artifacts-generate-aws-sdk-for-canary
141+
path: artifacts-generate-aws-sdk-for-canary
142+
- name: Extract artifacts
143+
run: tar xfz artifacts-generate-aws-sdk-for-canary/artifacts-generate-aws-sdk-for-canary.tar.gz
144+
- name: Install Rust
145+
uses: dtolnay/rust-toolchain@stable
146+
with:
147+
targets: aarch64-unknown-linux-musl
148+
- name: Install musl-tools
149+
run: sudo apt-get update && sudo apt-get install -y musl-tools
150+
- name: Configure credentials
151+
id: creds
152+
uses: aws-actions/configure-aws-credentials@v4
153+
with:
154+
role-to-assume: ${{ secrets.CANARY_GITHUB_ACTIONS_ROLE_ARN }}
155+
role-session-name: GitHubActions
156+
aws-region: us-west-2
157+
output-credentials: true
158+
- name: Run canary
159+
run: |
160+
export RUST_STABLE_VERSION="$(rustc --version | cut -d' ' -f2)"
161+
./smithy-rs/tools/ci-scripts/run-canary \
162+
${{ secrets.CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME }} \
163+
${{ steps.creds.outputs.aws-access-key-id }} \
164+
${{ steps.creds.outputs.aws-secret-access-key }} \
165+
${{ steps.creds.outputs.aws-session-token }} \
166+
aarch64

tools/ci-cdk/canary-runner/src/build_bundle.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,9 @@ fn name_hashed_bundle(
333333
rust_version: Option<&str>,
334334
sdk_release_tag: Option<&ReleaseTag>,
335335
) -> Result<String> {
336-
// The Lambda name must be less than 64 characters, so truncate the hash a bit
337-
let bin_hash = &bin_hash[..24];
336+
// The Lambda name must be less than 64 characters, so truncate the hash a bit.
337+
// Using 16 chars leaves headroom for the architecture suffix added in run.rs.
338+
let bin_hash = &bin_hash[..16];
338339
// Lambda function names can't have periods in them
339340
let rust_version = rust_version.map(|s| s.replace('.', ""));
340341
let rust_version = rust_version.as_deref().unwrap_or("unknown");
@@ -390,8 +391,8 @@ pub async fn build_bundle(opt: BuildBundleArgs) -> Result<Option<PathBuf>> {
390391
};
391392

392393
if !opt.manifest_only {
393-
// Check if cross is needed and available
394-
let use_cross = opt.architecture == Arch::Aarch64;
394+
// Only use cross when cross-compiling (host arch != target arch)
395+
let use_cross = opt.architecture == Arch::Aarch64 && std::env::consts::ARCH != "aarch64";
395396
if use_cross {
396397
let cross_check = Command::new("cross").arg("--version").output();
397398
if cross_check.is_err() || !cross_check.unwrap().status.success() {
@@ -883,7 +884,7 @@ aws-smithy-wasm = { version = "0.1.0" }
883884
assert_eq!(expected, actual);
884885
}
885886
check(
886-
"canary-release20221216-1621-7ae6085d2105d5d1e13b10f8.zip",
887+
"canary-release20221216-1621-7ae6085d2105d5d1.zip",
887888
&name_hashed_bundle(
888889
"7ae6085d2105d5d1e13b10f882c6cb072ff5bbf8",
889890
Some("1.62.1"),
@@ -892,7 +893,7 @@ aws-smithy-wasm = { version = "0.1.0" }
892893
.unwrap(),
893894
);
894895
check(
895-
"canary-release202212162-1621-7ae6085d2105d5d1e13b10f8.zip",
896+
"canary-release202212162-1621-7ae6085d2105d5d1.zip",
896897
&name_hashed_bundle(
897898
"7ae6085d2105d5d1e13b10f882c6cb072ff5bbf8",
898899
Some("1.62.1"),
@@ -901,7 +902,7 @@ aws-smithy-wasm = { version = "0.1.0" }
901902
.unwrap(),
902903
);
903904
check(
904-
"canary-untagged-1621-7ae6085d2105d5d1e13b10f8.zip",
905+
"canary-untagged-1621-7ae6085d2105d5d1.zip",
905906
&name_hashed_bundle(
906907
"7ae6085d2105d5d1e13b10f882c6cb072ff5bbf8",
907908
Some("1.62.1"),
@@ -910,7 +911,7 @@ aws-smithy-wasm = { version = "0.1.0" }
910911
.unwrap(),
911912
);
912913
check(
913-
"canary-release20221216-unknown-7ae6085d2105d5d1e13b10f8.zip",
914+
"canary-release20221216-unknown-7ae6085d2105d5d1.zip",
914915
&name_hashed_bundle(
915916
"7ae6085d2105d5d1e13b10f882c6cb072ff5bbf8",
916917
None,

tools/ci-cdk/canary-runner/src/run.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ pub struct RunArgs {
119119
/// The ARN of the role that the Lambda will execute as
120120
#[clap(long, required_unless_present = "cdk-output")]
121121
lambda_execution_role_arn: Option<String>,
122+
123+
/// Lambda architecture
124+
#[clap(long, default_value = "x86_64")]
125+
pub(crate) architecture: crate::arch::Arch,
122126
}
123127

124128
#[derive(Debug, Eq, PartialEq)]
@@ -134,6 +138,7 @@ struct Options {
134138
lambda_test_s3_mrap_bucket_arn: String,
135139
lambda_test_s3_express_bucket_name: String,
136140
lambda_execution_role_arn: String,
141+
architecture: crate::arch::Arch,
137142
}
138143

139144
impl Options {
@@ -203,6 +208,7 @@ impl Options {
203208
lambda_test_s3_mrap_bucket_arn,
204209
lambda_test_s3_express_bucket_name,
205210
lambda_execution_role_arn,
211+
architecture: run_opt.architecture,
206212
})
207213
} else {
208214
Ok(Options {
@@ -223,6 +229,7 @@ impl Options {
223229
.lambda_test_s3_express_bucket_name
224230
.expect("required"),
225231
lambda_execution_role_arn: run_opt.lambda_execution_role_arn.expect("required"),
232+
architecture: run_opt.architecture,
226233
})
227234
}
228235
}
@@ -319,9 +326,17 @@ async fn run_canary(options: &Options, config: &aws_config::SdkConfig) -> Result
319326
"Creating the canary Lambda function named {}...",
320327
bundle_name
321328
);
329+
let function_name = format!(
330+
"{}-{}",
331+
bundle_name,
332+
match options.architecture {
333+
crate::arch::Arch::X86_64 => "x86_64",
334+
crate::arch::Arch::Aarch64 => "aarch64",
335+
}
336+
);
322337
create_lambda_fn(
323338
lambda_client.clone(),
324-
bundle_name,
339+
&function_name,
325340
bundle_file_name,
326341
options,
327342
)
@@ -330,11 +345,11 @@ async fn run_canary(options: &Options, config: &aws_config::SdkConfig) -> Result
330345

331346
info!("Invoking the canary Lambda...");
332347
let invoke_start_time = SystemTime::now();
333-
let invoke_result = invoke_lambda(lambda_client.clone(), bundle_name).await;
348+
let invoke_result = invoke_lambda(lambda_client.clone(), &function_name).await;
334349
let invoke_time = invoke_start_time.elapsed().expect("time in range");
335350

336351
info!("Deleting the canary Lambda...");
337-
delete_lambda_fn(lambda_client, bundle_name)
352+
delete_lambda_fn(lambda_client, &function_name)
338353
.await
339354
.context(here!())?;
340355

@@ -362,7 +377,7 @@ async fn build_bundle(options: &Options) -> Result<PathBuf> {
362377
sdk_release_tag: options.sdk_release_tag.clone(),
363378
sdk_path: options.sdk_path.clone(),
364379
musl: options.musl,
365-
architecture: crate::arch::Arch::X86_64,
380+
architecture: options.architecture,
366381
manifest_only: false,
367382
disable_jitter_entropy: true,
368383
feature_override: None,
@@ -433,10 +448,20 @@ async fn create_lambda_fn(
433448
),
434449
};
435450

451+
let lambda_arch = match options.architecture {
452+
crate::arch::Arch::X86_64 => Architecture::X8664,
453+
crate::arch::Arch::Aarch64 => Architecture::Arm64,
454+
};
455+
456+
let lambda_runtime = match options.architecture {
457+
crate::arch::Arch::X86_64 => Runtime::Providedal2,
458+
crate::arch::Arch::Aarch64 => Runtime::Providedal2023,
459+
};
460+
436461
lambda_client
437462
.create_function()
438463
.function_name(bundle_name)
439-
.runtime(Runtime::Providedal2)
464+
.runtime(lambda_runtime)
440465
.role(&options.lambda_execution_role_arn)
441466
.handler("aws-sdk-rust-lambda-canary")
442467
.code(
@@ -449,6 +474,7 @@ async fn create_lambda_fn(
449474
.environment(env_builder.build())
450475
.timeout(180)
451476
.memory_size(options.lambda_function_memory_size_in_mb)
477+
.architectures(lambda_arch)
452478
.send()
453479
.await
454480
.context(here!("failed to create canary Lambda function"))?;
@@ -565,7 +591,8 @@ mod tests {
565591
lambda_test_s3_bucket_name: None,
566592
lambda_execution_role_arn: None,
567593
lambda_test_s3_mrap_bucket_arn: None,
568-
lambda_test_s3_express_bucket_name: None
594+
lambda_test_s3_express_bucket_name: None,
595+
architecture: crate::arch::Arch::X86_64,
569596
},
570597
RunArgs::try_parse_from([
571598
"run",
@@ -616,6 +643,7 @@ mod tests {
616643
lambda_test_s3_mrap_bucket_arn: "arn:aws:s3::000000000000:accesspoint/example.mrap"
617644
.to_owned(),
618645
lambda_test_s3_express_bucket_name: "test--usw2-az1--x-s3".to_owned(),
646+
architecture: crate::arch::Arch::X86_64,
619647
},
620648
Options::load_from(run_args).unwrap(),
621649
);

tools/ci-scripts/run-canary

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CANARY_STACK_CDK_OUTPUTS_BUCKET_NAME=$1
1010
export AWS_ACCESS_KEY_ID=$2
1111
export AWS_SECRET_ACCESS_KEY=$3
1212
export AWS_SESSION_TOKEN=$4
13+
ARCHITECTURE="${5:-x86_64}"
1314
export AWS_REGION=us-west-2
1415
export RUST_LOG=debug
1516

@@ -21,4 +22,5 @@ cargo run -- \
2122
run --rust-version "${RUST_STABLE_VERSION}" \
2223
--sdk-path "${SDK_PATH}" \
2324
--cdk-output cdk-outputs.json \
24-
--musl
25+
--musl \
26+
--architecture "${ARCHITECTURE}"

0 commit comments

Comments
 (0)