Skip to content

Commit da97e17

Browse files
committed
test: derive pinned binary verification targets
1 parent 5b36da8 commit da97e17

2 files changed

Lines changed: 100 additions & 39 deletions

File tree

src/binary_pins.rs

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ const MONGO_TRACER_INSTALLER: BinaryPin = BinaryPin {
112112
/// A binary the runner downloads at install time. The download helper looks
113113
/// up the URL and SHA-256 via `url()` and `sha256()` and rejects the install
114114
/// if the bytes don't match.
115-
#[derive(Debug, Clone, Copy)]
115+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116116
pub enum PinnedBinary {
117117
ValgrindDeb {
118118
distro_version: &'static str,
@@ -144,33 +144,6 @@ impl PinnedBinary {
144144
self.pin().url()
145145
}
146146

147-
/// Every `PinnedBinary` instance the runner can produce at runtime, used
148-
/// by the verification test to catch a stale or mistyped hash before
149-
/// release. Update this whenever a new variant or valgrind target is
150-
/// added.
151-
#[cfg(test)]
152-
pub const ALL: &'static [PinnedBinary] = &[
153-
PinnedBinary::ValgrindDeb {
154-
distro_version: "22.04",
155-
arch: "amd64",
156-
},
157-
PinnedBinary::ValgrindDeb {
158-
distro_version: "24.04",
159-
arch: "amd64",
160-
},
161-
PinnedBinary::ValgrindDeb {
162-
distro_version: "22.04",
163-
arch: "arm64",
164-
},
165-
PinnedBinary::ValgrindDeb {
166-
distro_version: "24.04",
167-
arch: "arm64",
168-
},
169-
PinnedBinary::MemtrackInstaller,
170-
PinnedBinary::ExecHarnessInstaller,
171-
PinnedBinary::MongoTracerInstaller,
172-
];
173-
174147
pub fn sha256(&self) -> &'static str {
175148
self.pin().sha256()
176149
}
@@ -182,21 +155,54 @@ mod tests {
182155
use crate::cli::run::helpers::download_pinned_file;
183156
use tempfile::NamedTempFile;
184157

158+
const INSTALLER_BINARIES: &[PinnedBinary] = &[
159+
PinnedBinary::MemtrackInstaller,
160+
PinnedBinary::ExecHarnessInstaller,
161+
PinnedBinary::MongoTracerInstaller,
162+
];
163+
164+
fn assert_installer_variant_is_listed(binary: PinnedBinary) {
165+
match binary {
166+
PinnedBinary::ValgrindDeb { .. } => {}
167+
PinnedBinary::MemtrackInstaller
168+
| PinnedBinary::ExecHarnessInstaller
169+
| PinnedBinary::MongoTracerInstaller => {
170+
assert!(INSTALLER_BINARIES.contains(&binary));
171+
}
172+
}
173+
}
174+
175+
fn all_pinned_binaries() -> impl Iterator<Item = PinnedBinary> {
176+
VALGRIND_DEBS
177+
.iter()
178+
.map(|pin| PinnedBinary::ValgrindDeb {
179+
distro_version: pin.distro_version,
180+
arch: pin.arch,
181+
})
182+
.chain(INSTALLER_BINARIES.iter().copied())
183+
}
184+
185+
#[test]
186+
fn installer_variant_list_is_exhaustive() {
187+
assert_installer_variant_is_listed(PinnedBinary::MemtrackInstaller);
188+
assert_installer_variant_is_listed(PinnedBinary::ExecHarnessInstaller);
189+
assert_installer_variant_is_listed(PinnedBinary::MongoTracerInstaller);
190+
}
191+
185192
// Network-bound: downloads every pinned URL and asserts its bytes hash to
186193
// the declared SHA-256. Skipped locally; CI sets `GITHUB_ACTIONS=true`.
187194
// Run after bumping a version to make sure the release won't ship a stale
188195
// or mistyped hash.
189196
#[test_with::env(GITHUB_ACTIONS)]
190197
#[tokio::test(flavor = "multi_thread")]
191198
async fn all_pinned_binaries_match_their_declared_sha256() {
192-
let results =
193-
futures::future::join_all(PinnedBinary::ALL.iter().map(|binary| async move {
194-
let temp = NamedTempFile::new().expect("failed to create temp file");
195-
download_pinned_file(*binary, temp.path())
196-
.await
197-
.map_err(|e| format!("{binary:?} ({}): {e}", binary.url()))
198-
}))
199-
.await;
199+
let results = futures::future::join_all(all_pinned_binaries().map(|binary| async move {
200+
let temp = NamedTempFile::new().expect("failed to create temp file");
201+
download_pinned_file(binary, temp.path())
202+
.await
203+
.map_err(|e| format!("{binary:?} ({}): {e}", binary.url()))
204+
}))
205+
.await;
200206

201207
let failures: Vec<_> = results.into_iter().filter_map(Result::err).collect();
202208
assert!(

src/executor/helpers/introspected_golang/go.sh

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,59 @@ debug_log() {
1111
debug_log "Called with arguments: $*"
1212
debug_log "Number of arguments: $#"
1313

14+
sha256_file() {
15+
if command -v sha256sum >/dev/null 2>&1; then
16+
sha256sum "$1" | awk '{ print $1 }'
17+
elif command -v shasum >/dev/null 2>&1; then
18+
shasum -a 256 "$1" | awk '{ print $1 }'
19+
else
20+
echo "ERROR: Could not find sha256sum or shasum to verify downloaded installer" >&2
21+
exit 1
22+
fi
23+
}
24+
25+
install_go_runner() {
26+
local download_url="$1"
27+
local release_api_url="$2"
28+
local tmp_dir
29+
tmp_dir=$(mktemp -d)
30+
local installer_path="$tmp_dir/codspeed-go-runner-installer.sh"
31+
local release_json_path="$tmp_dir/release.json"
32+
33+
cleanup_go_runner_installer() {
34+
rm -rf "$tmp_dir"
35+
}
36+
trap cleanup_go_runner_installer RETURN
37+
38+
curl -fsSL "$download_url" -o "$installer_path"
39+
curl -fsSL "$release_api_url" -o "$release_json_path"
40+
41+
local expected_sha256
42+
expected_sha256=$(awk '
43+
/"name": "codspeed-go-runner-installer.sh"/ { in_installer_asset = 1 }
44+
in_installer_asset && /"digest": "sha256:/ {
45+
sub(/^.*"digest": "sha256:/, "")
46+
sub(/".*$/, "")
47+
print
48+
found = 1
49+
exit
50+
}
51+
END { if (!found) exit 1 }
52+
' "$release_json_path") || {
53+
echo "ERROR: Could not find codspeed-go-runner-installer.sh digest in release metadata" >&2
54+
exit 1
55+
}
56+
57+
local actual_sha256
58+
actual_sha256=$(sha256_file "$installer_path")
59+
if [ "$actual_sha256" != "$expected_sha256" ]; then
60+
echo "ERROR: Hash mismatch for $download_url: expected $expected_sha256, got $actual_sha256" >&2
61+
exit 1
62+
fi
63+
64+
bash "$installer_path" --quiet
65+
}
66+
1467

1568
# Currently only walltime is supported
1669
if [ "${CODSPEED_RUNNER_MODE:-}" != "walltime" ]; then
@@ -50,14 +103,16 @@ case "$1" in
50103
# Build the installer URL with the specified version or use latest
51104
INSTALLER_VERSION="${CODSPEED_GO_RUNNER_VERSION:-latest}"
52105
if [ "$INSTALLER_VERSION" = "latest" ]; then
53-
DOWNLOAD_URL="http://github.com/CodSpeedHQ/codspeed-go/releases/latest/download/codspeed-go-runner-installer.sh"
106+
DOWNLOAD_URL="https://github.com/CodSpeedHQ/codspeed-go/releases/latest/download/codspeed-go-runner-installer.sh"
107+
RELEASE_API_URL="https://api.github.com/repos/CodSpeedHQ/codspeed-go/releases/latest"
54108
echo "::warning::Installing the latest version of codspeed-go-runner. This can silently introduce breaking changes. We recommend pinning a specific version via the \`go-runner-version\` option in the action." >&2
55109
else
56-
DOWNLOAD_URL="http://github.com/CodSpeedHQ/codspeed-go/releases/download/v${INSTALLER_VERSION}/codspeed-go-runner-installer.sh"
110+
DOWNLOAD_URL="https://github.com/CodSpeedHQ/codspeed-go/releases/download/v${INSTALLER_VERSION}/codspeed-go-runner-installer.sh"
111+
RELEASE_API_URL="https://api.github.com/repos/CodSpeedHQ/codspeed-go/releases/tags/v${INSTALLER_VERSION}"
57112
fi
58113

59114
debug_log "Installing go-runner from: $DOWNLOAD_URL"
60-
curl -fsSL "$DOWNLOAD_URL" | bash -s -- --quiet
115+
install_go_runner "$DOWNLOAD_URL" "$RELEASE_API_URL"
61116
GO_RUNNER=$(which codspeed-go-runner 2>/dev/null || true)
62117
fi
63118

0 commit comments

Comments
 (0)