From 76c1709aa1bb44ff08c0cd68fca2527be09cba7b Mon Sep 17 00:00:00 2001 From: "Wid L. Hacker" Date: Fri, 30 Oct 2026 06:25:35 -1000 Subject: [PATCH 01/14] Modernize big-tests WDL: Ubuntu 24.04, https CRAM reference, current Rust --- test/genomicsqlite_big_tests.wdl | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/test/genomicsqlite_big_tests.wdl b/test/genomicsqlite_big_tests.wdl index 665c7c21..ef9685ec 100644 --- a/test/genomicsqlite_big_tests.wdl +++ b/test/genomicsqlite_big_tests.wdl @@ -55,6 +55,11 @@ task build { command <<< set -euxo pipefail + # current Rust toolchain for the binding tests run under ctest + # (the distro cargo lags the versions pinned in Cargo.lock) + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal + export PATH="$HOME/.cargo/bin:$PATH" + # build libgenomicsqlite.so and loader executables git clone --recursive https://github.com/mlin/GenomicSQLite.git cd GenomicSQLite @@ -79,17 +84,17 @@ task build { } Array[String] apt_deps = [ - "zip", "pigz", "wget", "build-essential", "git-core", "sqlite3", "cmake", "libsqlite3-dev", - "libzstd-dev", "python3-pip", "maven", "cargo", "libhts-dev", "samtools", "tabix", "libcurl4-openssl-dev" + "zip", "pigz", "wget", "build-essential", "git", "sqlite3", "cmake", "libsqlite3-dev", + "libzstd-dev", "python3-pip", "maven", "libhts-dev", "samtools", "tabix", "libcurl4-openssl-dev" ] Array[String] pip_deps = ["pytest", "pytest-xdist"] runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:20.04", + "FROM ubuntu:24.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}", - "RUN pip3 install ~{sep(' ', pip_deps)}" + "RUN pip3 install --break-system-packages ~{sep(' ', pip_deps)}" ] } } @@ -97,7 +102,7 @@ task build { task test_sam { input { File reads - String cram_ref_fa_gz = "ftp://ftp.ncbi.nlm.nih.gov/genomes/all/GCA/000/001/405/GCA_000001405.15_GRCh38/seqs_for_alignment_pipelines.ucsc_ids/GCA_000001405.15_GRCh38_no_alt_analysis_set.fna.gz" + String cram_ref_fa_gz = "https://ftp.ncbi.nlm.nih.gov/genomes/all/GCA/000/001/405/GCA_000001405.15_GRCh38/seqs_for_alignment_pipelines.ucsc_ids/GCA_000001405.15_GRCh38_no_alt_analysis_set.fna.gz" File genomicsqlite_py File libgenomicsqlite_so @@ -110,7 +115,8 @@ task test_sam { set -euxo pipefail TMPDIR=${TMPDIR:-/tmp} - cp ~{genomicsqlite_py} /usr/lib/python3.8/genomicsqlite.py + GENOMICSQLITE_PY="$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')/genomicsqlite.py" + cp ~{genomicsqlite_py} "$GENOMICSQLITE_PY" cp ~{libgenomicsqlite_so} /usr/local/lib/libgenomicsqlite.so ldconfig >&2 sha256sum /usr/local/lib/libgenomicsqlite.so @@ -135,7 +141,7 @@ task test_sam { >&2 ls -l '~{dbname}' # compaction - time /usr/lib/python3.8/genomicsqlite.py '~{dbname}' --compact --level 9 --inner-page-KiB 64 --outer-page-KiB 1 + time "$GENOMICSQLITE_PY" '~{dbname}' --compact --level 9 --inner-page-KiB 64 --outer-page-KiB 1 >&2 ls -l '~{dbname}'* # GRI query @@ -155,8 +161,8 @@ task test_sam { time sqlite3 '~{dbname}.compact' "SELECT meta1, count(*), avg(length(data)) FROM nested_vfs_zstd_pages GROUP BY meta1" >&2 # add a QNAME-sorted seqs table. TODO: write it into a separate attached db - chmod +x /usr/lib/python3.8/genomicsqlite.py - time /usr/lib/python3.8/genomicsqlite.py '~{dbname}.compact' "PRAGMA journal_mode=off; PRAGMA synchronous=off; CREATE TABLE reads_seqs_by_qname AS SELECT * from reads_seqs NOT INDEXED ORDER BY qname" + chmod +x "$GENOMICSQLITE_PY" + time "$GENOMICSQLITE_PY" '~{dbname}.compact' "PRAGMA journal_mode=off; PRAGMA synchronous=off; CREATE TABLE reads_seqs_by_qname AS SELECT * from reads_seqs NOT INDEXED ORDER BY qname" >>> output { @@ -170,7 +176,7 @@ task test_sam { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:20.04", + "FROM ubuntu:24.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" ] } @@ -264,7 +270,7 @@ task test_sam_web { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:20.04", + "FROM ubuntu:24.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" ] } @@ -284,7 +290,8 @@ task test_vcf { command <<< set -euxo pipefail - cp ~{genomicsqlite_py} /usr/lib/python3.8/genomicsqlite.py + GENOMICSQLITE_PY="$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')/genomicsqlite.py" + cp ~{genomicsqlite_py} "$GENOMICSQLITE_PY" cp ~{libgenomicsqlite_so} /usr/local/lib/libgenomicsqlite.so ldconfig >&2 sha256sum /usr/local/lib/libgenomicsqlite.so @@ -324,7 +331,7 @@ task test_vcf { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:20.04", + "FROM ubuntu:24.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" ] } From a53a05a059ed857217071568c2fa4b0866989fa4 Mon Sep 17 00:00:00 2001 From: "Wid L. Hacker" Date: Fri, 30 Oct 2026 08:18:27 -1000 Subject: [PATCH 02/14] Big-tests WDL: use stock noble cargo (dual-track Rust floor), drop rustup --- test/genomicsqlite_big_tests.wdl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/genomicsqlite_big_tests.wdl b/test/genomicsqlite_big_tests.wdl index ef9685ec..90661ec2 100644 --- a/test/genomicsqlite_big_tests.wdl +++ b/test/genomicsqlite_big_tests.wdl @@ -55,10 +55,6 @@ task build { command <<< set -euxo pipefail - # current Rust toolchain for the binding tests run under ctest - # (the distro cargo lags the versions pinned in Cargo.lock) - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal - export PATH="$HOME/.cargo/bin:$PATH" # build libgenomicsqlite.so and loader executables git clone --recursive https://github.com/mlin/GenomicSQLite.git @@ -85,7 +81,7 @@ task build { Array[String] apt_deps = [ "zip", "pigz", "wget", "build-essential", "git", "sqlite3", "cmake", "libsqlite3-dev", - "libzstd-dev", "python3-pip", "maven", "libhts-dev", "samtools", "tabix", "libcurl4-openssl-dev" + "libzstd-dev", "python3-pip", "maven", "cargo", "libhts-dev", "samtools", "tabix", "libcurl4-openssl-dev" ] Array[String] pip_deps = ["pytest", "pytest-xdist"] From 62058f14cf6288e8d80298e99e72527416bb893d Mon Sep 17 00:00:00 2001 From: "Wid L. Hacker" Date: Sat, 31 Oct 2026 08:07:08 -1000 Subject: [PATCH 03/14] Big-tests WDL: stage genomicsqlite.py in the workdir instead of computing site-packages --- test/genomicsqlite_big_tests.wdl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/genomicsqlite_big_tests.wdl b/test/genomicsqlite_big_tests.wdl index 90661ec2..0436c648 100644 --- a/test/genomicsqlite_big_tests.wdl +++ b/test/genomicsqlite_big_tests.wdl @@ -111,7 +111,7 @@ task test_sam { set -euxo pipefail TMPDIR=${TMPDIR:-/tmp} - GENOMICSQLITE_PY="$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')/genomicsqlite.py" + GENOMICSQLITE_PY="$PWD/genomicsqlite.py" cp ~{genomicsqlite_py} "$GENOMICSQLITE_PY" cp ~{libgenomicsqlite_so} /usr/local/lib/libgenomicsqlite.so ldconfig @@ -286,7 +286,7 @@ task test_vcf { command <<< set -euxo pipefail - GENOMICSQLITE_PY="$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')/genomicsqlite.py" + GENOMICSQLITE_PY="$PWD/genomicsqlite.py" cp ~{genomicsqlite_py} "$GENOMICSQLITE_PY" cp ~{libgenomicsqlite_so} /usr/local/lib/libgenomicsqlite.so ldconfig From 8c6f73ffca7bfffef50eff7200f34266bd20d6b5 Mon Sep 17 00:00:00 2001 From: "Wid L. Hacker" Date: Sat, 31 Oct 2026 08:12:03 -1000 Subject: [PATCH 04/14] Big-tests WDL: invoke genomicsqlite.py via python3, drop the chmod (OpenAI consult) --- test/genomicsqlite_big_tests.wdl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/genomicsqlite_big_tests.wdl b/test/genomicsqlite_big_tests.wdl index 0436c648..e71b713b 100644 --- a/test/genomicsqlite_big_tests.wdl +++ b/test/genomicsqlite_big_tests.wdl @@ -137,7 +137,7 @@ task test_sam { >&2 ls -l '~{dbname}' # compaction - time "$GENOMICSQLITE_PY" '~{dbname}' --compact --level 9 --inner-page-KiB 64 --outer-page-KiB 1 + time python3 "$GENOMICSQLITE_PY" '~{dbname}' --compact --level 9 --inner-page-KiB 64 --outer-page-KiB 1 >&2 ls -l '~{dbname}'* # GRI query @@ -157,8 +157,7 @@ task test_sam { time sqlite3 '~{dbname}.compact' "SELECT meta1, count(*), avg(length(data)) FROM nested_vfs_zstd_pages GROUP BY meta1" >&2 # add a QNAME-sorted seqs table. TODO: write it into a separate attached db - chmod +x "$GENOMICSQLITE_PY" - time "$GENOMICSQLITE_PY" '~{dbname}.compact' "PRAGMA journal_mode=off; PRAGMA synchronous=off; CREATE TABLE reads_seqs_by_qname AS SELECT * from reads_seqs NOT INDEXED ORDER BY qname" + time python3 "$GENOMICSQLITE_PY" '~{dbname}.compact' "PRAGMA journal_mode=off; PRAGMA synchronous=off; CREATE TABLE reads_seqs_by_qname AS SELECT * from reads_seqs NOT INDEXED ORDER BY qname" >>> output { From 5f06efd79c02a0aba62532dfcb7e41e4e1189b95 Mon Sep 17 00:00:00 2001 From: "Wid L. Hacker" Date: Sat, 31 Oct 2026 11:00:03 -1000 Subject: [PATCH 05/14] Big-tests WDL: install python3 in the sam/vcf/web test images --- test/genomicsqlite_big_tests.wdl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/genomicsqlite_big_tests.wdl b/test/genomicsqlite_big_tests.wdl index e71b713b..f8ecedd6 100644 --- a/test/genomicsqlite_big_tests.wdl +++ b/test/genomicsqlite_big_tests.wdl @@ -166,7 +166,7 @@ task test_sam { Int reads_original_size = round(size(reads)) } - Array[String] apt_deps = ["sqlite3", "samtools", "tabix", "libzstd1", "pigz", "wget"] + Array[String] apt_deps = ["sqlite3", "python3", "samtools", "tabix", "libzstd1", "pigz", "wget"] runtime { cpu: 8 @@ -260,7 +260,7 @@ task test_sam_web { killall nginx || true >>> - Array[String] apt_deps = ["sqlite3", "nginx", "openssl", "python3-minimal", "libcurl4", "psmisc"] + Array[String] apt_deps = ["sqlite3", "nginx", "openssl", "python3", "libcurl4", "psmisc"] runtime { cpu: 8 @@ -321,7 +321,7 @@ task test_vcf { Int variants_original_size = round(size(variants)) } - Array[String] apt_deps = ["sqlite3", "samtools", "tabix", "libzstd1", "pigz", "wget"] + Array[String] apt_deps = ["sqlite3", "python3", "samtools", "tabix", "libzstd1", "pigz", "wget"] runtime { cpu: 8 From f25acd22451b0b79b0b8a325edc14eabe454474b Mon Sep 17 00:00:00 2001 From: "Wid L. Hacker" Date: Sat, 31 Oct 2026 12:35:45 -1000 Subject: [PATCH 06/14] Big-tests WDL: move task images to ubuntu:26.04 (CPython 3.14) --- test/genomicsqlite_big_tests.wdl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/genomicsqlite_big_tests.wdl b/test/genomicsqlite_big_tests.wdl index f8ecedd6..adf7ff5f 100644 --- a/test/genomicsqlite_big_tests.wdl +++ b/test/genomicsqlite_big_tests.wdl @@ -88,7 +88,7 @@ task build { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:24.04", + "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}", "RUN pip3 install --break-system-packages ~{sep(' ', pip_deps)}" ] @@ -171,7 +171,7 @@ task test_sam { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:24.04", + "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" ] } @@ -265,7 +265,7 @@ task test_sam_web { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:24.04", + "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" ] } @@ -326,7 +326,7 @@ task test_vcf { runtime { cpu: 8 inlineDockerfile: [ - "FROM ubuntu:24.04", + "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" ] } From 0fe44a37e2a108197046a70ff6c48c8ad7cc32a9 Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sat, 31 Oct 2026 14:36:43 -1000 Subject: [PATCH 07/14] Add EC2 runner for big tests --- test/big-tests/.gitignore | 5 + test/big-tests/.terraform.lock.hcl | 25 +++ test/big-tests/README.md | 85 ++++++++++ .../genomicsqlite_big_tests.wdl | 0 test/big-tests/main.tf | 149 ++++++++++++++++++ test/big-tests/scratch-setup.sh | 43 +++++ test/big-tests/test_scratch_setup.py | 102 ++++++++++++ test/big-tests/user-data.sh.tftpl | 65 ++++++++ 8 files changed, 474 insertions(+) create mode 100644 test/big-tests/.gitignore create mode 100644 test/big-tests/.terraform.lock.hcl create mode 100644 test/big-tests/README.md rename test/{ => big-tests}/genomicsqlite_big_tests.wdl (100%) create mode 100644 test/big-tests/main.tf create mode 100644 test/big-tests/scratch-setup.sh create mode 100644 test/big-tests/test_scratch_setup.py create mode 100644 test/big-tests/user-data.sh.tftpl diff --git a/test/big-tests/.gitignore b/test/big-tests/.gitignore new file mode 100644 index 00000000..a43e5da9 --- /dev/null +++ b/test/big-tests/.gitignore @@ -0,0 +1,5 @@ +.terraform/ +*.tfstate +*.tfstate.* +*.tfvars +crash.log diff --git a/test/big-tests/.terraform.lock.hcl b/test/big-tests/.terraform.lock.hcl new file mode 100644 index 00000000..39a6fbd3 --- /dev/null +++ b/test/big-tests/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.100.0" + constraints = "~> 5.0" + hashes = [ + "h1:edXOJWE4ORX8Fm+dpVpICzMZJat4AX0VRCAy/xkcOc0=", + "zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644", + "zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2", + "zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274", + "zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b", + "zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862", + "zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93", + "zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2", + "zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e", + "zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421", + "zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4", + "zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9", + "zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9", + "zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70", + ] +} diff --git a/test/big-tests/README.md b/test/big-tests/README.md new file mode 100644 index 00000000..f7542a99 --- /dev/null +++ b/test/big-tests/README.md @@ -0,0 +1,85 @@ +# Big tests on one EC2 instance + +This directory contains the big-test WDL and a small Terraform configuration for +running it with miniwdl on an on-demand `c7gd.2xlarge` in the default VPC. The +instance has eight Arm64 vCPUs, 16 GiB RAM, one 474 GB NVMe instance-store disk, +and a separate 50 GiB encrypted gp3 root volume. The default region is +`us-west-2`; `-var='region=...'` can select another region with a default VPC, +default subnet, and this instance type. If needed, use +`-var='availability_zone=us-west-2a'` to choose an availability zone; otherwise +Terraform selects a default subnet. The configuration does not include AWS +account IDs, VPC IDs, credentials, or SSH keys. + +Terraform creates an outbound-only security group and an instance profile with +the AWS-documented minimum permissions for Systems Manager Session Manager. +There is no inbound SSH rule and no S3 access in the instance role. The caller +needs Terraform's usual EC2 and IAM permissions, including permission to pass +the role, and separate permission to start a Session Manager session. The AWS +CLI's Session Manager plugin is needed on the client. This uses the standard +Ubuntu 26.04 Arm64 AMI from Canonical's public SSM parameter; a default subnet +with internet access and an available public IPv4 address are required. + +Review the plan before creating billable resources: + +```sh +terraform init +terraform plan +terraform apply +``` + +Connect with the command printed by `terraform output -raw session_command`. +The first boot installs Docker, containerd, and miniwdl, so wait for cloud-init +to finish before starting a run: + +```sh +cloud-init status --wait +sudo -iu ubuntu +docker info --format '{{.DockerRootDir}}' +miniwdl --version +``` + +Docker's root should be `/scratch/docker`. Both Docker and containerd depend on +the scratch setup service, and containerd's data root is `/scratch/containerd`. +The service identifies the instance store by its NVMe model, formats it only +if it has no existing signatures or partitions, and mounts it at `/scratch`. +It preserves the filesystem on reboot and refuses unexpected disk layouts. +Inspect failures with `journalctl -u genomicsqlite-scratch` and +`/var/log/cloud-init-output.log`. Docker group membership takes effect in a new +login; `sudo -iu ubuntu` provides one. Membership in that group permits root +access on the host. + +Clone this repository and check out the WDL revision to run, for example: + +```sh +git clone https://github.com/mlin/GenomicSQLite.git +cd GenomicSQLite +# Check out the desired WDL revision here. +miniwdl check test/big-tests/genomicsqlite_big_tests.wdl +miniwdl run_self_test +miniwdl run --dir /scratch/miniwdl/runs test/big-tests/genomicsqlite_big_tests.wdl +``` + +The WDL's `git_revision` input separately chooses which GenomicSQLite revision +the build task checks out; its default is `main`. Set it explicitly when the +tested source revision matters. The WDL uses miniwdl's `inlineDockerfile` +extension; bootstrap installs miniwdl 1.16.0 and the Docker backend. Both the +instance OS and the task images use Ubuntu 26.04. + +The system miniwdl configuration enables call and download caching under +`/scratch/miniwdl`, and places task temporary files on the run filesystem. +The call cache references files in earlier run directories, so keep those +directories while using the cache. Storage is finite: the default CRAM and VCF +alone occupy about 33 GiB, before the intermediate BAM and databases. Check +`df -h /scratch` during the run. The GCS VCF and other public downloads require +internet access; the public S3 CRAM resides in `us-east-1`. + +Instance-store contents, including Docker images, caches, and run files, are +lost on stop, hibernation, termination, or host replacement. Copy anything to +retain to durable storage before stopping the instance. The instance profile +does not grant bucket access; add narrowly scoped permissions separately if +you intend to upload to S3. Terraform state is local by default and may contain +infrastructure details; retain it securely until cleanup. When finished: + +```sh +terraform destroy +``` diff --git a/test/genomicsqlite_big_tests.wdl b/test/big-tests/genomicsqlite_big_tests.wdl similarity index 100% rename from test/genomicsqlite_big_tests.wdl rename to test/big-tests/genomicsqlite_big_tests.wdl diff --git a/test/big-tests/main.tf b/test/big-tests/main.tf new file mode 100644 index 00000000..30539a10 --- /dev/null +++ b/test/big-tests/main.tf @@ -0,0 +1,149 @@ +terraform { + required_version = ">= 1.5.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.region +} + +data "aws_vpc" "default" { + default = true +} + +data "aws_subnets" "default" { + filter { + name = "vpc-id" + values = [data.aws_vpc.default.id] + } + filter { + name = "default-for-az" + values = ["true"] + } + dynamic "filter" { + for_each = var.availability_zone == null ? [] : [var.availability_zone] + content { + name = "availability-zone" + values = [filter.value] + } + } +} + +data "aws_ssm_parameter" "ubuntu_arm64" { + name = "/aws/service/canonical/ubuntu/server/resolute/stable/current/arm64/hvm/ebs-gp3/ami-id" +} + +data "aws_iam_policy_document" "assume_ec2" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["ec2.amazonaws.com"] + } + } +} + +resource "aws_iam_role" "instance" { + name_prefix = "genomicsqlite-big-tests-" + assume_role_policy = data.aws_iam_policy_document.assume_ec2.json +} + +# The minimum AWS-documented permissions for Session Manager. These APIs do +# not support resource-level restrictions; no S3 or Parameter Store access is +# granted to the instance. The person starting a session needs their own IAM +# permission to do so. +data "aws_iam_policy_document" "session_manager" { + statement { + actions = [ + "ssm:UpdateInstanceInformation", + "ssmmessages:CreateControlChannel", + "ssmmessages:CreateDataChannel", + "ssmmessages:OpenControlChannel", + "ssmmessages:OpenDataChannel", + ] + resources = ["*"] + } +} + +resource "aws_iam_role_policy" "session_manager" { + name = "session-manager" + role = aws_iam_role.instance.id + policy = data.aws_iam_policy_document.session_manager.json +} + +resource "aws_iam_instance_profile" "instance" { + name_prefix = "genomicsqlite-big-tests-" + role = aws_iam_role.instance.name +} + +resource "aws_security_group" "instance" { + name_prefix = "genomicsqlite-big-tests-" + description = "Outbound access for GenomicSQLite big tests; access via Session Manager" + vpc_id = data.aws_vpc.default.id + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_instance" "runner" { + ami = data.aws_ssm_parameter.ubuntu_arm64.value + instance_type = "c7gd.2xlarge" + subnet_id = sort(data.aws_subnets.default.ids)[0] + associate_public_ip_address = true + vpc_security_group_ids = [aws_security_group.instance.id] + iam_instance_profile = aws_iam_instance_profile.instance.name + + root_block_device { + volume_type = "gp3" + volume_size = 50 + encrypted = true + delete_on_termination = true + } + + metadata_options { + http_endpoint = "enabled" + http_tokens = "required" + } + + user_data = templatefile("${path.module}/user-data.sh.tftpl", { + scratch_setup = filebase64("${path.module}/scratch-setup.sh") + }) + user_data_replace_on_change = true + + tags = { + Name = "genomicsqlite-big-tests" + } + + depends_on = [aws_iam_role_policy.session_manager] +} + +variable "region" { + description = "AWS region containing the default VPC and public Ubuntu AMI parameter." + type = string + default = "us-west-2" +} + +variable "availability_zone" { + description = "Optional availability zone for its default subnet; otherwise select a default subnet automatically." + type = string + default = null +} + +output "instance_id" { + value = aws_instance.runner.id +} + +output "session_command" { + description = "Connect using AWS CLI and the Session Manager plugin." + value = "aws ssm start-session --region ${var.region} --target ${aws_instance.runner.id}" +} diff --git a/test/big-tests/scratch-setup.sh b/test/big-tests/scratch-setup.sh new file mode 100644 index 00000000..f8df509b --- /dev/null +++ b/test/big-tests/scratch-setup.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -euo pipefail + +# NVMe names are not stable: both the EBS root and the instance store use them. +# Refuse anything except exactly one disk identified by its AWS model string. +devices=() +for model_file in /sys/block/nvme*n1/device/model; do + [[ -f "$model_file" ]] || continue + if [[ $(<"$model_file") == "Amazon EC2 NVMe Instance Storage"* ]]; then + disk_dir=$(dirname "$(dirname "$model_file")") + devices+=("/dev/$(basename "$disk_dir")") + fi +done +if (( ${#devices[@]} != 1 )); then + echo "Expected exactly one NVMe instance-store disk; found ${#devices[@]}" >&2 + exit 1 +fi +device=${devices[0]} + +mkdir -p /scratch +if mountpoint -q /scratch; then + [[ $(findmnt -n -o MAJ:MIN --target /scratch) == "$(lsblk -dn -o MAJ:MIN "$device")" ]] +else + # Only format an empty, unpartitioned device. Preserve the filesystem on + # reboot, and refuse unexpected signatures instead of destroying them. + if [[ $(lsblk -nr -o TYPE "$device") != disk ]]; then + echo "Instance-store disk has partitions; refusing to format $device" >&2 + exit 1 + fi + signatures=$(wipefs --noheadings --output TYPE "$device") + if [[ -z "$signatures" ]]; then + mkfs.ext4 -m 0 -L gsql-scratch "$device" + elif [[ "$signatures" != ext4 ]] || [[ $(blkid -s TYPE -o value "$device") != ext4 ]] || + [[ $(blkid -s LABEL -o value "$device") != gsql-scratch ]]; then + echo "Unexpected filesystem or signatures on $device; refusing to format" >&2 + exit 1 + fi + mount "$device" /scratch +fi + +install -d -m 0755 /scratch/docker /scratch/containerd +install -d -o ubuntu -g ubuntu -m 0755 /scratch/miniwdl /scratch/miniwdl/runs \ + /scratch/miniwdl/download-cache /scratch/miniwdl/call-cache diff --git a/test/big-tests/test_scratch_setup.py b/test/big-tests/test_scratch_setup.py new file mode 100644 index 00000000..f2e14ad2 --- /dev/null +++ b/test/big-tests/test_scratch_setup.py @@ -0,0 +1,102 @@ +"""Exercise scratch-disk safety decisions without touching real block devices.""" + +import os +from pathlib import Path +import subprocess +import tempfile +import unittest + + +MOCK_COMMAND = r"""#!/bin/bash +set -e +case "$(basename "$0")" in + mountpoint) test -e "$TEST_ROOT/mounted" ;; + findmnt) echo 259:1 ;; + lsblk) + if [[ "$*" == *MAJ:MIN* ]]; then echo 259:1 + elif [[ -e "$TEST_ROOT/partitioned" ]]; then printf 'disk\npart\n' + else echo disk; fi ;; + wipefs) + if [[ -e "$TEST_ROOT/badfs" ]]; then echo xfs + elif [[ -e "$TEST_ROOT/fs" ]]; then echo ext4; fi ;; + blkid) if [[ "$*" == *TYPE* ]]; then echo ext4; else echo gsql-scratch; fi ;; + mkfs.ext4) echo format >> "$TEST_ROOT/actions"; touch "$TEST_ROOT/fs" ;; + mount) echo mount >> "$TEST_ROOT/actions"; touch "$TEST_ROOT/mounted" ;; + install) : ;; + *) exit 99 ;; +esac +""" + + +class ScratchSetupTest(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="gsql-scratch-test-") + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + bindir = self.root / "bin" + bindir.mkdir() + for name in ( + "mountpoint", + "findmnt", + "lsblk", + "wipefs", + "blkid", + "mkfs.ext4", + "mount", + "install", + ): + command = bindir / name + command.write_text(MOCK_COMMAND) + command.chmod(0o755) + self.model = self.root / "sys/block/nvme1n1/device/model" + self.model.parent.mkdir(parents=True) + self.model.write_text("Amazon EC2 NVMe Instance Storage\n") + source = (Path(__file__).parent / "scratch-setup.sh").read_text() + self.script = self.root / "setup.sh" + self.script.write_text( + source.replace("/sys/block/", str(self.root) + "/sys/block/") + .replace("/dev/", str(self.root) + "/dev/") + .replace("/scratch", str(self.root) + "/scratch") + ) + self.env = dict( + os.environ, TEST_ROOT=str(self.root), PATH=str(bindir) + ":" + os.environ["PATH"] + ) + + def run_setup(self): + return subprocess.run( + ["bash", str(self.script)], env=self.env, capture_output=True, text=True + ) + + def test_fresh_disk_and_reboot(self): + first = self.run_setup() + self.assertEqual(first.returncode, 0, first.stderr) + (self.root / "mounted").unlink() + reboot = self.run_setup() + self.assertEqual(reboot.returncode, 0, reboot.stderr) + self.assertEqual( + (self.root / "actions").read_text().splitlines(), ["format", "mount", "mount"] + ) + + def test_refuses_unexpected_disk_layout(self): + for marker in ("badfs", "partitioned"): + with self.subTest(marker=marker): + (self.root / marker).touch() + result = self.run_setup() + self.assertNotEqual(result.returncode, 0) + self.assertIn("refusing to format", result.stderr) + self.assertFalse((self.root / "actions").exists()) + (self.root / marker).unlink() + + def test_refuses_ebs_or_multiple_instance_store_disks(self): + self.model.write_text("Amazon Elastic Block Store\n") + self.assertNotEqual(self.run_setup().returncode, 0) + self.model.write_text("Amazon EC2 NVMe Instance Storage\n") + other = self.root / "sys/block/nvme2n1/device/model" + other.parent.mkdir(parents=True) + other.write_text(self.model.read_text()) + self.assertNotEqual(self.run_setup().returncode, 0) + self.assertFalse((self.root / "actions").exists()) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/big-tests/user-data.sh.tftpl b/test/big-tests/user-data.sh.tftpl new file mode 100644 index 00000000..ad0bf8e6 --- /dev/null +++ b/test/big-tests/user-data.sh.tftpl @@ -0,0 +1,65 @@ +#!/bin/bash +set -euxo pipefail + +install -d -m 0755 /usr/local/sbin /etc/systemd/system/docker.service.d \ + /etc/systemd/system/containerd.service.d /etc/docker /etc/containerd /etc/xdg +base64 -d > /usr/local/sbin/genomicsqlite-scratch <<'SCRATCH_SCRIPT' +${scratch_setup} +SCRATCH_SCRIPT +chmod 0755 /usr/local/sbin/genomicsqlite-scratch + +cat > /etc/systemd/system/genomicsqlite-scratch.service <<'UNIT' +[Unit] +Description=Prepare and mount GenomicSQLite instance-store scratch +After=local-fs.target +Before=containerd.service docker.service + +[Service] +Type=oneshot +ExecStartPre=/usr/bin/udevadm settle --timeout=30 +ExecStart=/usr/local/sbin/genomicsqlite-scratch +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +UNIT + +for service in docker containerd; do + cat > "/etc/systemd/system/$service.service.d/scratch.conf" <<'UNIT' +[Unit] +Requires=genomicsqlite-scratch.service +After=genomicsqlite-scratch.service +UNIT +done + +cat > /etc/docker/daemon.json <<'JSON' +{"data-root":"/scratch/docker"} +JSON +cat > /etc/containerd/config.toml <<'TOML' +version = 2 +root = "/scratch/containerd" +TOML +cat > /etc/xdg/miniwdl.cfg <<'CONFIG' +[file_io] +mount_tmpdir = true + +[download_cache] +get = true +put = true +dir = /scratch/miniwdl/download-cache + +[call_cache] +get = true +put = true +dir = /scratch/miniwdl/call-cache +CONFIG + +systemctl daemon-reload +systemctl enable --now genomicsqlite-scratch.service +apt-get update +DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io python3-venv git curl tmux +usermod -aG docker ubuntu +python3 -m venv /opt/miniwdl +/opt/miniwdl/bin/pip install 'miniwdl==1.16.0' +ln -sf /opt/miniwdl/bin/miniwdl /usr/local/bin/miniwdl +systemctl enable --now containerd docker From aa5187c0b9a3e19859f5c5c524cd9c28689ed6b7 Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sat, 31 Oct 2026 16:33:57 -1000 Subject: [PATCH 08/14] Use Arm-compatible miniwdl downloaders --- test/big-tests/README.md | 6 +++++ test/big-tests/main.tf | 3 ++- test/big-tests/miniwdl-tools.Dockerfile | 14 +++++++++++ test/big-tests/user-data.sh.tftpl | 32 ++++++++++++++++++++++++- 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/big-tests/miniwdl-tools.Dockerfile diff --git a/test/big-tests/README.md b/test/big-tests/README.md index f7542a99..37d6a933 100644 --- a/test/big-tests/README.md +++ b/test/big-tests/README.md @@ -65,6 +65,12 @@ tested source revision matters. The WDL uses miniwdl's `inlineDockerfile` extension; bootstrap installs miniwdl 1.16.0 and the Docker backend. Both the instance OS and the task images use Ubuntu 26.04. +The default miniwdl downloader image is amd64-only. Bootstrap builds a local +Arm64 image containing aria2 and AWS CLI and configures both HTTP/FTP and S3 +downloads to use it. An enabled systemd service rebuilds it if the instance +store is lost after a stop/start; image building requires internet access. For +Google Storage, miniwdl uses Google's multi-architecture Cloud CLI image. + The system miniwdl configuration enables call and download caching under `/scratch/miniwdl`, and places task temporary files on the run filesystem. The call cache references files in earlier run directories, so keep those diff --git a/test/big-tests/main.tf b/test/big-tests/main.tf index 30539a10..f67bf07d 100644 --- a/test/big-tests/main.tf +++ b/test/big-tests/main.tf @@ -116,7 +116,8 @@ resource "aws_instance" "runner" { } user_data = templatefile("${path.module}/user-data.sh.tftpl", { - scratch_setup = filebase64("${path.module}/scratch-setup.sh") + scratch_setup = filebase64("${path.module}/scratch-setup.sh") + downloader_image = filebase64("${path.module}/miniwdl-tools.Dockerfile") }) user_data_replace_on_change = true diff --git a/test/big-tests/miniwdl-tools.Dockerfile b/test/big-tests/miniwdl-tools.Dockerfile new file mode 100644 index 00000000..a8fcdf03 --- /dev/null +++ b/test/big-tests/miniwdl-tools.Dockerfile @@ -0,0 +1,14 @@ +# miniwdl 1.16.0's default tools image is amd64-only. This Arm64 equivalent +# provides the same two programs used by its HTTP/FTP and S3 downloaders. +FROM ubuntu:26.04 AS awscli_builder +RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y curl unzip ca-certificates +RUN curl -fsSL https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o /tmp/awscli.zip \ + && unzip -q /tmp/awscli.zip -d /tmp \ + && /tmp/aws/install --install-dir /opt/aws-cli --bin-dir /opt/aws-cli-bin + +FROM ubuntu:26.04 +RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y aria2 ca-certificates \ + && rm -rf /var/lib/apt/lists/* +COPY --from=awscli_builder /opt/aws-cli /opt/aws-cli +COPY --from=awscli_builder /opt/aws-cli-bin/ /usr/local/bin/ +RUN aria2c --version >/dev/null && aws --version diff --git a/test/big-tests/user-data.sh.tftpl b/test/big-tests/user-data.sh.tftpl index ad0bf8e6..0c83d9ea 100644 --- a/test/big-tests/user-data.sh.tftpl +++ b/test/big-tests/user-data.sh.tftpl @@ -2,11 +2,15 @@ set -euxo pipefail install -d -m 0755 /usr/local/sbin /etc/systemd/system/docker.service.d \ - /etc/systemd/system/containerd.service.d /etc/docker /etc/containerd /etc/xdg + /etc/systemd/system/containerd.service.d /etc/docker /etc/containerd /etc/xdg \ + /opt/genomicsqlite-downloader base64 -d > /usr/local/sbin/genomicsqlite-scratch <<'SCRATCH_SCRIPT' ${scratch_setup} SCRATCH_SCRIPT chmod 0755 /usr/local/sbin/genomicsqlite-scratch +base64 -d > /opt/genomicsqlite-downloader/Dockerfile <<'DOWNLOADER_DOCKERFILE' +${downloader_image} +DOWNLOADER_DOCKERFILE cat > /etc/systemd/system/genomicsqlite-scratch.service <<'UNIT' [Unit] @@ -39,6 +43,21 @@ cat > /etc/containerd/config.toml <<'TOML' version = 2 root = "/scratch/containerd" TOML +cat > /etc/systemd/system/genomicsqlite-downloader.service <<'UNIT' +[Unit] +Description=Build miniwdl's Arm64 downloader image if needed +Requires=docker.service +After=docker.service network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c '/usr/bin/docker image inspect genomicsqlite-miniwdl-tools:arm64 >/dev/null 2>&1 || exec /usr/bin/docker build -t genomicsqlite-miniwdl-tools:arm64 /opt/genomicsqlite-downloader' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +UNIT cat > /etc/xdg/miniwdl.cfg <<'CONFIG' [file_io] mount_tmpdir = true @@ -52,6 +71,15 @@ dir = /scratch/miniwdl/download-cache get = true put = true dir = /scratch/miniwdl/call-cache + +[download_aria2c] +docker = genomicsqlite-miniwdl-tools:arm64 + +[download_awscli] +docker = genomicsqlite-miniwdl-tools:arm64 + +[download_gsutil] +docker = gcr.io/google.com/cloudsdktool/google-cloud-cli:589.0.0-slim CONFIG systemctl daemon-reload @@ -63,3 +91,5 @@ python3 -m venv /opt/miniwdl /opt/miniwdl/bin/pip install 'miniwdl==1.16.0' ln -sf /opt/miniwdl/bin/miniwdl /usr/local/bin/miniwdl systemctl enable --now containerd docker +systemctl daemon-reload +systemctl enable --now genomicsqlite-downloader.service From cd9f36b1da2cc8a2934878cd598330a2069faf53 Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sat, 31 Oct 2026 19:39:51 -1000 Subject: [PATCH 09/14] Prepare big tests for larger ARM instance --- test/big-tests/README.md | 9 ++++++--- test/big-tests/genomicsqlite_big_tests.wdl | 14 +++++++------- test/big-tests/main.tf | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/test/big-tests/README.md b/test/big-tests/README.md index 37d6a933..108ce2b9 100644 --- a/test/big-tests/README.md +++ b/test/big-tests/README.md @@ -1,8 +1,8 @@ # Big tests on one EC2 instance This directory contains the big-test WDL and a small Terraform configuration for -running it with miniwdl on an on-demand `c7gd.2xlarge` in the default VPC. The -instance has eight Arm64 vCPUs, 16 GiB RAM, one 474 GB NVMe instance-store disk, +running it with miniwdl on an on-demand `c7gd.4xlarge` in the default VPC. The +instance has 16 Arm64 vCPUs, 32 GiB RAM, one 950 GB NVMe instance-store disk, and a separate 50 GiB encrypted gp3 root volume. The default region is `us-west-2`; `-var='region=...'` can select another region with a default VPC, default subnet, and this instance type. If needed, use @@ -75,7 +75,10 @@ The system miniwdl configuration enables call and download caching under `/scratch/miniwdl`, and places task temporary files on the run filesystem. The call cache references files in earlier run directories, so keep those directories while using the cache. Storage is finite: the default CRAM and VCF -alone occupy about 33 GiB, before the intermediate BAM and databases. Check +alone occupy about 33 GiB, before the intermediate BAM and databases. A trial +on a 474 GB instance store filled the disk during the SAM QNAME sort, which +creates large temporary files. The WDL requests all 16 vCPUs for each task so +miniwdl runs at most one at a time on this instance. Check `df -h /scratch` during the run. The GCS VCF and other public downloads require internet access; the public S3 CRAM resides in `us-east-1`. diff --git a/test/big-tests/genomicsqlite_big_tests.wdl b/test/big-tests/genomicsqlite_big_tests.wdl index adf7ff5f..197ccfdb 100644 --- a/test/big-tests/genomicsqlite_big_tests.wdl +++ b/test/big-tests/genomicsqlite_big_tests.wdl @@ -81,12 +81,12 @@ task build { Array[String] apt_deps = [ "zip", "pigz", "wget", "build-essential", "git", "sqlite3", "cmake", "libsqlite3-dev", - "libzstd-dev", "python3-pip", "maven", "cargo", "libhts-dev", "samtools", "tabix", "libcurl4-openssl-dev" + "libzstd-dev", "python3-pip", "maven", "cargo", "libhts-dev", "samtools", "tabix", "libcurl4-gnutls-dev" ] Array[String] pip_deps = ["pytest", "pytest-xdist"] runtime { - cpu: 8 + cpu: 16 inlineDockerfile: [ "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}", @@ -98,7 +98,7 @@ task build { task test_sam { input { File reads - String cram_ref_fa_gz = "https://ftp.ncbi.nlm.nih.gov/genomes/all/GCA/000/001/405/GCA_000001405.15_GRCh38/seqs_for_alignment_pipelines.ucsc_ids/GCA_000001405.15_GRCh38_no_alt_analysis_set.fna.gz" + String cram_ref_fa = "https://s3.amazonaws.com/1000genomes/technical/reference/GRCh38_reference_genome/GRCh38_full_analysis_set_plus_decoy_hla.fa" File genomicsqlite_py File libgenomicsqlite_so @@ -124,7 +124,7 @@ task test_sam { reads_file='~{reads}' if [[ $reads_file == *.cram ]]; then # CRAM given; make BAM to take reference downloads out of the timings - wget -nv -O - '~{cram_ref_fa_gz}' | pigz -dc > "${TMPDIR}/cram_ref.fa" + wget -nv -O "${TMPDIR}/cram_ref.fa" '~{cram_ref_fa}' samtools faidx "${TMPDIR}/cram_ref.fa" bam_file="${TMPDIR}/$(basename "$reads_file" .cram).bam" time samtools view -T "${TMPDIR}/cram_ref.fa" -h -O BAM -@ 8 "$reads_file" > "$bam_file" @@ -169,7 +169,7 @@ task test_sam { Array[String] apt_deps = ["sqlite3", "python3", "samtools", "tabix", "libzstd1", "pigz", "wget"] runtime { - cpu: 8 + cpu: 16 inlineDockerfile: [ "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" @@ -263,7 +263,7 @@ task test_sam_web { Array[String] apt_deps = ["sqlite3", "nginx", "openssl", "python3", "libcurl4", "psmisc"] runtime { - cpu: 8 + cpu: 16 inlineDockerfile: [ "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" @@ -324,7 +324,7 @@ task test_vcf { Array[String] apt_deps = ["sqlite3", "python3", "samtools", "tabix", "libzstd1", "pigz", "wget"] runtime { - cpu: 8 + cpu: 16 inlineDockerfile: [ "FROM ubuntu:26.04", "RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ~{sep(' ', apt_deps)}" diff --git a/test/big-tests/main.tf b/test/big-tests/main.tf index f67bf07d..0ee28f05 100644 --- a/test/big-tests/main.tf +++ b/test/big-tests/main.tf @@ -97,7 +97,7 @@ resource "aws_security_group" "instance" { resource "aws_instance" "runner" { ami = data.aws_ssm_parameter.ubuntu_arm64.value - instance_type = "c7gd.2xlarge" + instance_type = "c7gd.4xlarge" subnet_id = sort(data.aws_subnets.default.ids)[0] associate_public_ip_address = true vpc_security_group_ids = [aws_security_group.instance.id] From 6027cd3c73e43dd6b5240633edbed39414b5244c Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sun, 1 Nov 2026 14:43:34 -1000 Subject: [PATCH 10/14] Record successful ARM big-tests run --- .../runs/big-tests-run-2026-11-01.zip | Bin 0 -> 111738 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/big-tests/runs/big-tests-run-2026-11-01.zip diff --git a/test/big-tests/runs/big-tests-run-2026-11-01.zip b/test/big-tests/runs/big-tests-run-2026-11-01.zip new file mode 100644 index 0000000000000000000000000000000000000000..67885a2c2fc34bb09279fd72729239bd5dc75ecd GIT binary patch literal 111738 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 z z?5@nbUXXG+d%I~>bNl6Z^Wr;yiZJaNNcFD7r5}|fz2`ZGm=IkZwro~W?$R{x z)k%4|88ubEM8B$Sd@Dd(+;1xOnlnGSEugWX_|em=ceaeGe0hAs>exAzgI#UW10J0f z8*g1a(s{qUsxxI+fcdlG7rvgBS5n`PO|%TV81!~R?q3FGU#$E`%YRQyS^M}!Vsh!t zkaoD(py__YjHrfT4gI~FcU0rW9`b_P@3)>?6Yaa8FaP9>64|vK;hVBNHm&g+sM`N; z&o%4!%PJneUbJ~rtJmCb0u56w9+P0U_S^aqdx-rO!gv4L+xKh$ zmV2`=lKLbua{AvEfA`GwQGd_9dh*k2J3l!yx;ySrLiT4({_bbpqP>5~E}wRF!46Zq$ zdo_$vB>mgUm_buM$Qfm3rSTP)XFf7*)5E6zrNsRE(O*Bc<9>@7^BD&uozMT*Wq0O%GO%G z%gF+u4xV5J44)MF4s>=TupK+xD17a)z9Y9`hPNE@5oT`fMv*bF^i2Ys-4lnRX%;Ra_Mn>%b=s|eG>~LBoCKz~0 z!;1;!@RQH6cZT@%L7R+GWD2~!lE#;&6)t^_&;LOekjt9|+gg8sQRw&Zx{X^q^Ci=z zD26^@fH8`!18?P+FR9BGtxA@n4tk$5V-#5c7hE`FBr1X4$jcZ-ZiIuboKaFson9T! z7)8D`o{uH1gt;W22?8VurAaR{XN)3O@REc|uF#Ss^8i(R0FDGQ#%>gu0oW`(VGK^! z=&@|M3OB1UoKP75Y()LV>gYXn*0!l>73P|yn}=cP@B;J=3lO2}>$ znFQ^f6{LbR;Rd-%iEH&b#cPCKheBRb5BtypH?)j}_JwY88KcM#5r|rsq36q&f!`f$ z^n|=U1bNGi#%`#9j?pkfW2=UE@MP|Y%;7;R0df|i!mU~@&<8(Mv+SJ!&ZzIiU$@Y$weab&0kvzk3rZ1h!8kbyHR8fLQ!BD zPUYJ_JSOcPgxL+D$JoQt-g!GXYik_zgxr8@a3#T;h^(4}&m4!$yzPmK_2XGX00J{8 zjaZg&M)Wtde94H6n-LXoda^YVgx+MpaEORmEmSHapSz*Yh9uArDBo@rd5@u(bV{vL zDhi?d^@_Ui`WYzAZ@p}b#F)SoSsVcy?{T96R*MB&Ys>~~TCg0GJRC|#fe~g@rS~4k z9e5Kwdg^qW+*w%uJaHJG<_8szVkd#hYLD9X0kO2$g)EIw8sv1Ga-afXdV^CU={~00 zT}y%%ifSZuMYv^M`Kl9%Pq1Ct52yPou$p|~<_$W&=i`fvvmh@lXXx+5$r&uJe9(f)hrlbq(`dJ9i}1 z&V?XkZd`&UJ3#CgtzvquR;hCuu-%fa+8YUo*XP&Z+$l$Z#9eo)fl3WKgVkR-Q<)Edag64n8FM=(Ur zY7{wkmHpB`im3&?|BiuDq~8KDrm$)pk4E%oaPUcyO^bhnD4-tn!XgGr zk)z=RE0}@`#*`Y3obdtnQWsJYdZ7%F*Tt_Av0i#O#2P0G(B-upS(^yrov4VdiEq6&z*l1 zX9{TwJs^-JE=fa)^cH3TC7~y?u^1nW4UF!@XFP4(CIOX$3te zi)HCE_*lXULHFLXB-|iC67V$W)^nD0l;9%?sR7;d%W}4whdAsGV6;1kZkTZhyww(% z**=4YL^sc{A!~<1ifpApVU8XG-YnXUA`chaq9Nj5&{b(nXo~Ex2X Date: Sun, 1 Nov 2026 14:59:38 -1000 Subject: [PATCH 11/14] Organize AWS runner and improve runbook monitoring --- test/big-tests/README.md | 94 ----------- test/big-tests/{ => aws}/.gitignore | 0 test/big-tests/{ => aws}/.terraform.lock.hcl | 0 test/big-tests/aws/AGENTS.md | 1 + test/big-tests/aws/README.md | 157 ++++++++++++++++++ test/big-tests/aws/cloudwatch-agent.json | 28 ++++ test/big-tests/{ => aws}/main.tf | 26 ++- .../{ => aws}/miniwdl-tools.Dockerfile | 0 test/big-tests/{ => aws}/scratch-setup.sh | 0 .../big-tests/{ => aws}/test_scratch_setup.py | 0 test/big-tests/{ => aws}/user-data.sh.tftpl | 6 + 11 files changed, 217 insertions(+), 95 deletions(-) delete mode 100644 test/big-tests/README.md rename test/big-tests/{ => aws}/.gitignore (100%) rename test/big-tests/{ => aws}/.terraform.lock.hcl (100%) create mode 120000 test/big-tests/aws/AGENTS.md create mode 100644 test/big-tests/aws/README.md create mode 100644 test/big-tests/aws/cloudwatch-agent.json rename test/big-tests/{ => aws}/main.tf (85%) rename test/big-tests/{ => aws}/miniwdl-tools.Dockerfile (100%) rename test/big-tests/{ => aws}/scratch-setup.sh (100%) rename test/big-tests/{ => aws}/test_scratch_setup.py (100%) rename test/big-tests/{ => aws}/user-data.sh.tftpl (84%) diff --git a/test/big-tests/README.md b/test/big-tests/README.md deleted file mode 100644 index 108ce2b9..00000000 --- a/test/big-tests/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# Big tests on one EC2 instance - -This directory contains the big-test WDL and a small Terraform configuration for -running it with miniwdl on an on-demand `c7gd.4xlarge` in the default VPC. The -instance has 16 Arm64 vCPUs, 32 GiB RAM, one 950 GB NVMe instance-store disk, -and a separate 50 GiB encrypted gp3 root volume. The default region is -`us-west-2`; `-var='region=...'` can select another region with a default VPC, -default subnet, and this instance type. If needed, use -`-var='availability_zone=us-west-2a'` to choose an availability zone; otherwise -Terraform selects a default subnet. The configuration does not include AWS -account IDs, VPC IDs, credentials, or SSH keys. - -Terraform creates an outbound-only security group and an instance profile with -the AWS-documented minimum permissions for Systems Manager Session Manager. -There is no inbound SSH rule and no S3 access in the instance role. The caller -needs Terraform's usual EC2 and IAM permissions, including permission to pass -the role, and separate permission to start a Session Manager session. The AWS -CLI's Session Manager plugin is needed on the client. This uses the standard -Ubuntu 26.04 Arm64 AMI from Canonical's public SSM parameter; a default subnet -with internet access and an available public IPv4 address are required. - -Review the plan before creating billable resources: - -```sh -terraform init -terraform plan -terraform apply -``` - -Connect with the command printed by `terraform output -raw session_command`. -The first boot installs Docker, containerd, and miniwdl, so wait for cloud-init -to finish before starting a run: - -```sh -cloud-init status --wait -sudo -iu ubuntu -docker info --format '{{.DockerRootDir}}' -miniwdl --version -``` - -Docker's root should be `/scratch/docker`. Both Docker and containerd depend on -the scratch setup service, and containerd's data root is `/scratch/containerd`. -The service identifies the instance store by its NVMe model, formats it only -if it has no existing signatures or partitions, and mounts it at `/scratch`. -It preserves the filesystem on reboot and refuses unexpected disk layouts. -Inspect failures with `journalctl -u genomicsqlite-scratch` and -`/var/log/cloud-init-output.log`. Docker group membership takes effect in a new -login; `sudo -iu ubuntu` provides one. Membership in that group permits root -access on the host. - -Clone this repository and check out the WDL revision to run, for example: - -```sh -git clone https://github.com/mlin/GenomicSQLite.git -cd GenomicSQLite -# Check out the desired WDL revision here. -miniwdl check test/big-tests/genomicsqlite_big_tests.wdl -miniwdl run_self_test -miniwdl run --dir /scratch/miniwdl/runs test/big-tests/genomicsqlite_big_tests.wdl -``` - -The WDL's `git_revision` input separately chooses which GenomicSQLite revision -the build task checks out; its default is `main`. Set it explicitly when the -tested source revision matters. The WDL uses miniwdl's `inlineDockerfile` -extension; bootstrap installs miniwdl 1.16.0 and the Docker backend. Both the -instance OS and the task images use Ubuntu 26.04. - -The default miniwdl downloader image is amd64-only. Bootstrap builds a local -Arm64 image containing aria2 and AWS CLI and configures both HTTP/FTP and S3 -downloads to use it. An enabled systemd service rebuilds it if the instance -store is lost after a stop/start; image building requires internet access. For -Google Storage, miniwdl uses Google's multi-architecture Cloud CLI image. - -The system miniwdl configuration enables call and download caching under -`/scratch/miniwdl`, and places task temporary files on the run filesystem. -The call cache references files in earlier run directories, so keep those -directories while using the cache. Storage is finite: the default CRAM and VCF -alone occupy about 33 GiB, before the intermediate BAM and databases. A trial -on a 474 GB instance store filled the disk during the SAM QNAME sort, which -creates large temporary files. The WDL requests all 16 vCPUs for each task so -miniwdl runs at most one at a time on this instance. Check -`df -h /scratch` during the run. The GCS VCF and other public downloads require -internet access; the public S3 CRAM resides in `us-east-1`. - -Instance-store contents, including Docker images, caches, and run files, are -lost on stop, hibernation, termination, or host replacement. Copy anything to -retain to durable storage before stopping the instance. The instance profile -does not grant bucket access; add narrowly scoped permissions separately if -you intend to upload to S3. Terraform state is local by default and may contain -infrastructure details; retain it securely until cleanup. When finished: - -```sh -terraform destroy -``` diff --git a/test/big-tests/.gitignore b/test/big-tests/aws/.gitignore similarity index 100% rename from test/big-tests/.gitignore rename to test/big-tests/aws/.gitignore diff --git a/test/big-tests/.terraform.lock.hcl b/test/big-tests/aws/.terraform.lock.hcl similarity index 100% rename from test/big-tests/.terraform.lock.hcl rename to test/big-tests/aws/.terraform.lock.hcl diff --git a/test/big-tests/aws/AGENTS.md b/test/big-tests/aws/AGENTS.md new file mode 120000 index 00000000..42061c01 --- /dev/null +++ b/test/big-tests/aws/AGENTS.md @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/test/big-tests/aws/README.md b/test/big-tests/aws/README.md new file mode 100644 index 00000000..64cb95e8 --- /dev/null +++ b/test/big-tests/aws/README.md @@ -0,0 +1,157 @@ +# Big tests on one EC2 instance + +After each use, update this runbook with what will make future runs smoother and +repeatable. Keep it concise and broadly applicable; avoid unnecessary detail +and instructions that depend on incidental choices from a particular run. + +This Terraform configuration runs the [big-test WDL](../genomicsqlite_big_tests.wdl) +with miniwdl on an on-demand `c7gd.4xlarge` in the default VPC. The instance +has 16 Arm64 vCPUs, 32 GiB RAM, a 950 GB NVMe instance store, and a separate +50 GiB encrypted gp3 root volume. The default region is `us-west-2`. + +A successful trial took about 4 hours 12 minutes. The WDL requests 16 CPUs per +task, so miniwdl runs one at a time; that is a scheduling reservation, not a +promise that each task uses every CPU. The previous 474 GB instance store +filled during the SAM QNAME sort; the larger trial reached at least 547 GiB of +scratch usage. The inputs alone occupy about 33 GiB. Expect different usage +when inputs or software change. + +## Deploy and connect + +The configuration uses Canonical's public Ubuntu 26.04 Arm64 AMI, a default +VPC and subnet, internet access and a public IPv4 address. It creates an +outbound-only security group and an instance role for Session Manager and +CloudWatch metrics; it does not +require SSH keys or account-specific IDs. The caller needs Terraform's EC2 and +IAM permissions (including `iam:PassRole`), Session Manager permission, and +the AWS CLI Session Manager plugin. Set `region` or `availability_zone` with +Terraform variables if the defaults are unsuitable. + +From the repository root: + +```sh +cd test/big-tests/aws +terraform init +terraform plan +terraform apply +# Run the command printed by: +terraform output -raw session_command +``` + +On the instance, wait for bootstrap and enter a fresh Ubuntu login so Docker +group membership takes effect: + +```sh +cloud-init status --wait +sudo -iu ubuntu +findmnt /scratch +df -h /scratch +docker info --format '{{.DockerRootDir}}' +grep '^root' /etc/containerd/config.toml +miniwdl --version +``` + +The instance-store device should be mounted at `/scratch`, with Docker at +`/scratch/docker` and containerd at `/scratch/containerd`. The scratch setup +formats a blank instance store, preserves its filesystem on reboot, and refuses +unexpected disk layouts. Docker and containerd depend on that setup. If boot +fails, inspect `journalctl -u genomicsqlite-scratch`, +`journalctl -u genomicsqlite-downloader`, and `/var/log/cloud-init-output.log`. +Docker group membership grants root-equivalent access. + +## Monitor in CloudWatch + +Bootstrap installs the CloudWatch agent and publishes host-wide CPU, memory, +`/scratch` capacity, and block-device I/O metrics every 10 seconds to the +`GenomicSQLite/BigTests` namespace. In CloudWatch Metrics, select that +namespace, filter by the instance ID printed by Terraform, and graph CPU +`usage_active`/`usage_iowait`, memory `used_percent`/`available_percent`, +`disk_used_percent`/`disk_free` for `/scratch`, and `diskio_*` for the NVMe +instance-store device shown by `findmnt /scratch` and `lsblk`. For disk I/O +byte and time counters, use CloudWatch's **Sum** statistic for each period; +for CPU, memory and capacity use **Average** or **Maximum** as appropriate. +These are host metrics rather than per-task measurements. + +Check `systemctl status amazon-cloudwatch-agent` and the agent log under +`/opt/aws/amazon-cloudwatch-agent/logs/` if metrics do not appear. The +instance role can publish only to this namespace and can describe EC2 tags; +it cannot write CloudWatch logs. The 10-second samples are billed as custom +metrics and may create multiple series for disk devices. CloudWatch keeps +sub-minute data for only three hours, then rolls it up to one-minute data; +export during the run if the full 10-second history matters. The ordinary +`AWS/EC2` CPU metric remains available separately at five-minute resolution. + +For a durable local 10-second copy spanning a multi-hour run, optionally use +`sar -A -o /scratch/miniwdl/sysstat.sa 10` in a detached tmux session (install +`sysstat` if needed). Stop it with Ctrl-C after the run. Its raw file includes +the hostname and must be sanitized before publication. + +## Run + +Choose the WDL revision and source revision deliberately. They are separate: +the WDL's `git_revision` input selects the GenomicSQLite source to build and +defaults to the moving `main` branch. The build task clones +`https://github.com/mlin/GenomicSQLite.git`, so the chosen source commit must +be available there. The following example resolves `main` to a commit; fetch +the WDL revision from its fork first if it has not been merged. + +```sh +git clone https://github.com/mlin/GenomicSQLite.git +cd GenomicSQLite +# Fetch if necessary, then check out the desired WDL revision. +git rev-parse HEAD # record the WDL revision +miniwdl check test/big-tests/genomicsqlite_big_tests.wdl +(cd /scratch/miniwdl && miniwdl run_self_test) +``` + +Run in tmux so an interrupted Session Manager connection does not stop the +workflow. Within the tmux session, record the console output and exit status: + +```sh +tmux new-session -s big-tests +# Inside tmux, from the GenomicSQLite checkout: +set -o pipefail +source_rev=$(git ls-remote origin refs/heads/main | cut -f1) +test -n "$source_rev" || { echo 'Could not resolve source revision' >&2; exit 1; } +printf 'source revision: %s\n' "$source_rev" +miniwdl run --no-color --dir /scratch/miniwdl/runs \ + test/big-tests/genomicsqlite_big_tests.wdl "git_revision=$source_rev" \ + 2>&1 | tee /scratch/miniwdl/big-tests-console.log +printf '%s\n' "${PIPESTATUS[0]}" > /scratch/miniwdl/big-tests.exit +``` + +Detach with `Ctrl-b`, then `d`; reconnect with `tmux attach -t big-tests`. +Monitor `df -h /scratch`, the console log, and the task logs in the run +directory. The workflow prints query results but does not assert their expected +values, so a successful exit is a stability check, not a full correctness +proof. The `.dbi` 404 in the no-index web test is expected fallback behavior. + +miniwdl 1.16.0 uses the Docker backend and the experimental `inlineDockerfile` +extension. Bootstrap builds an Arm64 aria2/AWS downloader image because the +default image is amd64-only; Google Storage uses the multi-architecture Google +Cloud CLI image. Inputs and successful calls are cached under +`/scratch/miniwdl`; call-cache entries reference earlier run directories, so +keep those directories for retries. Cache lookup does not validate mutable +remote URIs or image tags; use immutable revisions or disable cache for a +fresh comparison. The public GCS input and S3 CRAM require internet access; +the CRAM is in `us-east-1`. + +## Preserve results and clean up + +Before teardown, save the workflow and task logs, inputs/outputs, WDL and +source revisions, timings, system configuration, exported CloudWatch metrics, and a run-directory +size inventory. The databases and input files are large and can be omitted if +they are throwaway. A sanitized example is in [runs](../runs/). Check logs +and sysstat data for account IDs, hostnames, network details, and other +identifiers before publishing. The instance role has no S3 upload permissions; +use a separately authorized transfer or add narrowly scoped permissions. + +Instance-store contents—including Docker images, caches, logs, and run +files—are lost on stop, hibernation, termination, or host replacement. Preserve +anything needed **before** stopping the instance. Terraform state is local and +may contain infrastructure details. When finished, from the repository root: + +```sh +cd test/big-tests/aws +terraform destroy +``` diff --git a/test/big-tests/aws/cloudwatch-agent.json b/test/big-tests/aws/cloudwatch-agent.json new file mode 100644 index 00000000..2df9e05d --- /dev/null +++ b/test/big-tests/aws/cloudwatch-agent.json @@ -0,0 +1,28 @@ +{ + "agent": { + "metrics_collection_interval": 10 + }, + "metrics": { + "namespace": "GenomicSQLite/BigTests", + "append_dimensions": { + "InstanceId": "${aws:InstanceId}" + }, + "metrics_collected": { + "cpu": { + "totalcpu": true, + "measurement": ["usage_active", "usage_iowait"] + }, + "mem": { + "measurement": ["used_percent", "available_percent"] + }, + "disk": { + "resources": ["/scratch"], + "measurement": ["used_percent", "free"] + }, + "diskio": { + "resources": ["*"], + "measurement": ["read_bytes", "write_bytes", "io_time"] + } + } + } +} diff --git a/test/big-tests/main.tf b/test/big-tests/aws/main.tf similarity index 85% rename from test/big-tests/main.tf rename to test/big-tests/aws/main.tf index 0ee28f05..4fb060d4 100644 --- a/test/big-tests/main.tf +++ b/test/big-tests/aws/main.tf @@ -77,6 +77,29 @@ resource "aws_iam_role_policy" "session_manager" { policy = data.aws_iam_policy_document.session_manager.json } +resource "aws_iam_role_policy" "metrics" { + name = "publish-metrics" + role = aws_iam_role.instance.id + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = "cloudwatch:PutMetricData" + Resource = "*" + Condition = { + StringEquals = { "cloudwatch:namespace" = "GenomicSQLite/BigTests" } + } + }, + { + Effect = "Allow" + Action = "ec2:DescribeTags" + Resource = "*" + } + ] + }) +} + resource "aws_iam_instance_profile" "instance" { name_prefix = "genomicsqlite-big-tests-" role = aws_iam_role.instance.name @@ -118,6 +141,7 @@ resource "aws_instance" "runner" { user_data = templatefile("${path.module}/user-data.sh.tftpl", { scratch_setup = filebase64("${path.module}/scratch-setup.sh") downloader_image = filebase64("${path.module}/miniwdl-tools.Dockerfile") + metrics_config = filebase64("${path.module}/cloudwatch-agent.json") }) user_data_replace_on_change = true @@ -125,7 +149,7 @@ resource "aws_instance" "runner" { Name = "genomicsqlite-big-tests" } - depends_on = [aws_iam_role_policy.session_manager] + depends_on = [aws_iam_role_policy.session_manager, aws_iam_role_policy.metrics] } variable "region" { diff --git a/test/big-tests/miniwdl-tools.Dockerfile b/test/big-tests/aws/miniwdl-tools.Dockerfile similarity index 100% rename from test/big-tests/miniwdl-tools.Dockerfile rename to test/big-tests/aws/miniwdl-tools.Dockerfile diff --git a/test/big-tests/scratch-setup.sh b/test/big-tests/aws/scratch-setup.sh similarity index 100% rename from test/big-tests/scratch-setup.sh rename to test/big-tests/aws/scratch-setup.sh diff --git a/test/big-tests/test_scratch_setup.py b/test/big-tests/aws/test_scratch_setup.py similarity index 100% rename from test/big-tests/test_scratch_setup.py rename to test/big-tests/aws/test_scratch_setup.py diff --git a/test/big-tests/user-data.sh.tftpl b/test/big-tests/aws/user-data.sh.tftpl similarity index 84% rename from test/big-tests/user-data.sh.tftpl rename to test/big-tests/aws/user-data.sh.tftpl index 0c83d9ea..009b5636 100644 --- a/test/big-tests/user-data.sh.tftpl +++ b/test/big-tests/aws/user-data.sh.tftpl @@ -11,6 +11,9 @@ chmod 0755 /usr/local/sbin/genomicsqlite-scratch base64 -d > /opt/genomicsqlite-downloader/Dockerfile <<'DOWNLOADER_DOCKERFILE' ${downloader_image} DOWNLOADER_DOCKERFILE +base64 -d > /etc/amazon-cloudwatch-agent-big-tests.json <<'METRICS_CONFIG' +${metrics_config} +METRICS_CONFIG cat > /etc/systemd/system/genomicsqlite-scratch.service <<'UNIT' [Unit] @@ -86,6 +89,9 @@ systemctl daemon-reload systemctl enable --now genomicsqlite-scratch.service apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io python3-venv git curl tmux +curl -fsSL https://amazoncloudwatch-agent.s3.amazonaws.com/ubuntu/arm64/latest/amazon-cloudwatch-agent.deb -o /tmp/amazon-cloudwatch-agent.deb +DEBIAN_FRONTEND=noninteractive apt-get install -y /tmp/amazon-cloudwatch-agent.deb +/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/etc/amazon-cloudwatch-agent-big-tests.json usermod -aG docker ubuntu python3 -m venv /opt/miniwdl /opt/miniwdl/bin/pip install 'miniwdl==1.16.0' From 8e5eeaa4595b720d365aeff52850cfd8382e5c7d Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sun, 1 Nov 2026 15:05:16 -1000 Subject: [PATCH 12/14] Keep AWS runbook focused on repeatable guidance --- test/big-tests/aws/README.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/test/big-tests/aws/README.md b/test/big-tests/aws/README.md index 64cb95e8..18dee408 100644 --- a/test/big-tests/aws/README.md +++ b/test/big-tests/aws/README.md @@ -9,23 +9,20 @@ with miniwdl on an on-demand `c7gd.4xlarge` in the default VPC. The instance has 16 Arm64 vCPUs, 32 GiB RAM, a 950 GB NVMe instance store, and a separate 50 GiB encrypted gp3 root volume. The default region is `us-west-2`. -A successful trial took about 4 hours 12 minutes. The WDL requests 16 CPUs per -task, so miniwdl runs one at a time; that is a scheduling reservation, not a -promise that each task uses every CPU. The previous 474 GB instance store -filled during the SAM QNAME sort; the larger trial reached at least 547 GiB of -scratch usage. The inputs alone occupy about 33 GiB. Expect different usage -when inputs or software change. +Allow several hours and at least 550 GiB of scratch space for the default +inputs; actual usage can be higher, especially during the SAM QNAME sort. The +WDL requests 16 CPUs per task, so miniwdl runs one at a time, although a task +may use fewer CPUs. ## Deploy and connect The configuration uses Canonical's public Ubuntu 26.04 Arm64 AMI, a default VPC and subnet, internet access and a public IPv4 address. It creates an outbound-only security group and an instance role for Session Manager and -CloudWatch metrics; it does not -require SSH keys or account-specific IDs. The caller needs Terraform's EC2 and -IAM permissions (including `iam:PassRole`), Session Manager permission, and -the AWS CLI Session Manager plugin. Set `region` or `availability_zone` with -Terraform variables if the defaults are unsuitable. +CloudWatch metrics; it does not require SSH keys or account-specific IDs. The +caller needs Terraform's EC2 and IAM permissions (including `iam:PassRole`), +Session Manager permission, and the AWS CLI Session Manager plugin. Set +`region` or `availability_zone` with Terraform variables if needed. From the repository root: @@ -93,7 +90,7 @@ the WDL's `git_revision` input selects the GenomicSQLite source to build and defaults to the moving `main` branch. The build task clones `https://github.com/mlin/GenomicSQLite.git`, so the chosen source commit must be available there. The following example resolves `main` to a commit; fetch -the WDL revision from its fork first if it has not been merged. +the WDL revision from its source if it is not in the clone. ```sh git clone https://github.com/mlin/GenomicSQLite.git @@ -139,9 +136,9 @@ the CRAM is in `us-east-1`. ## Preserve results and clean up Before teardown, save the workflow and task logs, inputs/outputs, WDL and -source revisions, timings, system configuration, exported CloudWatch metrics, and a run-directory -size inventory. The databases and input files are large and can be omitted if -they are throwaway. A sanitized example is in [runs](../runs/). Check logs +source revisions, timings, system configuration, exported CloudWatch metrics, +and a run-directory size inventory. Omit the large databases and inputs if they +are throwaway. A sanitized example is in [runs](../runs/). Check logs and sysstat data for account IDs, hostnames, network details, and other identifiers before publishing. The instance role has no S3 upload permissions; use a separately authorized transfer or add narrowly scoped permissions. From b42f19a401635b49f2e829b72ea86953703c33de Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sun, 1 Nov 2026 15:06:53 -1000 Subject: [PATCH 13/14] Retain input size guidance in runbook --- test/big-tests/aws/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/big-tests/aws/README.md b/test/big-tests/aws/README.md index 18dee408..0a6ea246 100644 --- a/test/big-tests/aws/README.md +++ b/test/big-tests/aws/README.md @@ -10,9 +10,9 @@ has 16 Arm64 vCPUs, 32 GiB RAM, a 950 GB NVMe instance store, and a separate 50 GiB encrypted gp3 root volume. The default region is `us-west-2`. Allow several hours and at least 550 GiB of scratch space for the default -inputs; actual usage can be higher, especially during the SAM QNAME sort. The -WDL requests 16 CPUs per task, so miniwdl runs one at a time, although a task -may use fewer CPUs. +inputs, which occupy about 33 GiB; actual scratch usage can be higher, +especially during the SAM QNAME sort. The WDL requests 16 CPUs per task, so +miniwdl runs one at a time, although a task may use fewer CPUs. ## Deploy and connect From c19feace7948d79151a250f8d8c906ae134895cd Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Sun, 1 Nov 2026 15:12:24 -1000 Subject: [PATCH 14/14] Fix scratch setup test lint --- test/big-tests/aws/test_scratch_setup.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/big-tests/aws/test_scratch_setup.py b/test/big-tests/aws/test_scratch_setup.py index f2e14ad2..1fb1346c 100644 --- a/test/big-tests/aws/test_scratch_setup.py +++ b/test/big-tests/aws/test_scratch_setup.py @@ -1,11 +1,10 @@ """Exercise scratch-disk safety decisions without touching real block devices.""" import os -from pathlib import Path import subprocess import tempfile import unittest - +from pathlib import Path MOCK_COMMAND = r"""#!/bin/bash set -e @@ -64,7 +63,7 @@ def setUp(self): def run_setup(self): return subprocess.run( - ["bash", str(self.script)], env=self.env, capture_output=True, text=True + ["bash", str(self.script)], check=False, env=self.env, capture_output=True, text=True ) def test_fresh_disk_and_reboot(self):