Skip to content

Commit

Permalink
heroku-24 and multi-arch integration testing (#265)
Browse files Browse the repository at this point in the history
* Multi-arch integration tests

* Add arch-specific tests

* Add some clippy fixes

* Apply a cargo fix

* Drop colon from yaml

* Fix yaml indentation
  • Loading branch information
joshwlewis authored May 16, 2024
1 parent 589c6a6 commit e518444
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 121 deletions.
30 changes: 26 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,42 @@ jobs:
run: cargo test --locked

integration-test:
runs-on: ubuntu-22.04
name: integration-tests ${{ matrix.builder }} / ${{ matrix.arch }}
runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-22.04-arm-large' || 'ubuntu-latest' }}
strategy:
matrix:
arch: ["amd64"]
builder: ["heroku/builder:20", "heroku/builder:22", "heroku/builder:24"]
include:
- arch: "arm64"
builder: "heroku/builder:24"
steps:
- name: Checkout
uses: actions/checkout@v4
# The beta ARM64 runners don't yet ship with the normal installed tools.
- name: Install Docker, Rust and development libs (ARM64 only)
if: matrix.arch == 'arm64'
run: |
sudo apt-get update --error-on=any
sudo apt-get install -y --no-install-recommends acl docker.io docker-buildx libc6-dev
sudo usermod -aG docker $USER
sudo setfacl --modify user:$USER:rw /var/run/docker.sock
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
echo "${HOME}/.cargo/bin" >> "${GITHUB_PATH}"
- name: Install musl-tools
run: sudo apt-get install musl-tools --no-install-recommends
run: sudo apt-get install musl-tools -y --no-install-recommends
- name: Update Rust toolchain
run: rustup update
- name: Install Rust linux-musl target
run: rustup target add x86_64-unknown-linux-musl
run: rustup target add ${{ matrix.arch == 'arm64' && 'aarch64-unknown-linux-musl' || 'x86_64-unknown-linux-musl' }}
- name: Rust Cache
uses: Swatinem/[email protected]
- name: Install Pack CLI
uses: buildpacks/github-actions/[email protected]
- name: Pull builder image
run: docker pull ${{ matrix.builder }}
- name: Run integration tests
env:
INTEGRATION_TEST_BUILDER: ${{ matrix.builder }}
# Runs only tests annotated with the `ignore` attribute (which in this repo, are the integration tests).
run: cargo test --locked -- --ignored
run: cargo test --locked -- --ignored --test-threads 16
218 changes: 101 additions & 117 deletions buildpacks/go/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,79 @@
#![allow(unused_crate_dependencies)]

use libcnb_test::{assert_contains, assert_not_contains, BuildConfig, ContainerConfig, TestRunner};
use std::time::Duration;

fn test_go_fixture(
fixture: &str,
builder: &str,
expect_loglines: &[&str],
refute_loglines: &[&str],
) {
TestRunner::default().build(
BuildConfig::new(builder, format!("tests/fixtures/{fixture}")),
|ctx| {
let logs = format!("{}\n{}", ctx.pack_stdout, ctx.pack_stderr);
for expect_line in expect_loglines {
assert_contains!(logs, expect_line);
}
for refute_line in refute_loglines {
assert_not_contains!(logs, refute_line);
}

let port = 8080;
ctx.start_container(ContainerConfig::new().expose_port(port), |container| {
std::thread::sleep(Duration::from_secs(5));
let addr = container.address_for_port(port);
let resp = ureq::get(&format!("http://{addr}"))
.call()
.expect("request to container failed")
.into_string()
.expect("response read error");

assert_contains!(resp, fixture);
});
},
);
use std::{env::consts, time::Duration};

const DEFAULT_BUILDER: &str = "heroku/builder:24";

struct IntegrationTestConfig {
target: String,
builder: String,
fixture: String,
}

impl IntegrationTestConfig {
fn new<S: Into<String>>(fixture: S) -> Self {
let builder =
std::env::var("INTEGRATION_TEST_BUILDER").unwrap_or(DEFAULT_BUILDER.to_string());
let target = match (builder.as_str(), consts::ARCH) {
// Compile the buildpack for arm64 if the builder supports multi-arch and the host is ARM64.
// This happens in CI and on developer machines with Apple silicon.
("heroku/builder:24", "aarch64") => "aarch64-unknown-linux-musl".to_string(),
// Compile the buildpack for arm64 if an arm64-specific builder is chosen.
// Used to run cross-arch integration tests from machines with Intel silicon.
(b, _) if b.ends_with("arm64") => "aarch64-unknown-linux-musl".to_string(),
(_, _) => "x86_64-unknown-linux-musl".to_string(),
};
let fixture = format!("tests/fixtures/{}", fixture.into());
Self {
target,
builder,
fixture,
}
}
}

impl From<IntegrationTestConfig> for BuildConfig {
fn from(integration_test_config: IntegrationTestConfig) -> BuildConfig {
let mut build_config = BuildConfig::new(
integration_test_config.builder,
integration_test_config.fixture,
);
build_config.target_triple(integration_test_config.target);
build_config
}
}

fn test_go_fixture(fixture: &str, expect_loglines: &[&str], refute_loglines: &[&str]) {
TestRunner::default().build(&IntegrationTestConfig::new(fixture).into(), |ctx| {
let logs = format!("{}\n{}", ctx.pack_stdout, ctx.pack_stderr);
for expect_line in expect_loglines {
assert_contains!(logs, expect_line);
}
for refute_line in refute_loglines {
assert_not_contains!(logs, refute_line);
}

let port = 8080;
ctx.start_container(ContainerConfig::new().expose_port(port), |container| {
std::thread::sleep(Duration::from_secs(5));
let addr = container.address_for_port(port);
let resp = ureq::get(&format!("http://{addr}"))
.call()
.expect("request to container failed")
.into_string()
.expect("response read error");

assert_contains!(resp, fixture);
});
});
}

fn test_basic_http_116(builder: &str) {
#[test]
#[ignore = "integration test"]
fn test_basic_http_116() {
test_go_fixture(
"basic_http_116",
builder,
&[
"Detected Go version requirement: ~1.16.2",
"Resolved Go version: go1.16.",
Expand All @@ -49,21 +83,12 @@ fn test_basic_http_116(builder: &str) {
&[],
);
}

#[test]
#[ignore = "integration test"]
fn basic_http_116_20() {
test_basic_http_116("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn basic_http_116_22() {
test_basic_http_116("heroku/builder:22");
}

fn test_vendor_gorilla_117(builder: &str) {
fn test_vendor_gorilla_117() {
test_go_fixture(
"vendor_gorilla_117",
builder,
&[
"Detected Go version requirement: =1.17.8",
"Installing go1.17.8",
Expand All @@ -72,21 +97,12 @@ fn test_vendor_gorilla_117(builder: &str) {
&["downloading github.com/gorilla/mux v1.8.0"],
);
}

#[test]
#[ignore = "integration test"]
fn vendor_gorilla_117_20() {
test_vendor_gorilla_117("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn vendor_gorilla_117_22() {
test_vendor_gorilla_117("heroku/builder:22");
}

fn test_modules_gin_121(builder: &str) {
fn test_modules_gin_121() {
test_go_fixture(
"modules_gin_121",
builder,
&[
"Detected Go version requirement: =1.21",
"Installing go1.21",
Expand All @@ -95,21 +111,12 @@ fn test_modules_gin_121(builder: &str) {
&[],
);
}

#[test]
#[ignore = "integration test"]
fn modules_gin_121_20() {
test_modules_gin_121("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn modules_gin_121_22() {
test_modules_gin_121("heroku/builder:22");
}

fn test_worker_http_118(builder: &str) {
fn test_worker_http_118() {
test_go_fixture(
"worker_http_118",
builder,
&[
"Detected Go version requirement: ~1.18.1",
"Installing go1.18.",
Expand All @@ -119,43 +126,25 @@ fn test_worker_http_118(builder: &str) {
&["example.com/worker_http_118/cmd/script"],
);
}

#[test]
#[ignore = "integration test"]
fn worker_http_118_20() {
test_worker_http_118("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn worker_http_118_22() {
test_worker_http_118("heroku/builder:22");
}

fn test_basic_http_119(builder: &str) {
fn test_basic_http_119() {
test_go_fixture(
"basic_http_119",
builder,
&[
"Detected Go version requirement: ~1.19.4",
"Installing go1.19.",
],
&[],
);
}

#[test]
#[ignore = "integration test"]
fn basic_http_119_20() {
test_basic_http_119("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn basic_http_119_22() {
test_basic_http_119("heroku/builder:22");
}

fn test_vendor_fasthttp_120(builder: &str) {
fn test_vendor_fasthttp_120() {
test_go_fixture(
"vendor_fasthttp_120",
builder,
&[
"Detected Go version requirement: =1.20",
"Installing go1.20.",
Expand All @@ -164,53 +153,48 @@ fn test_vendor_fasthttp_120(builder: &str) {
&["downloading github.com/valyala/fasthttp"],
);
}

#[test]
#[ignore = "integration test"]
fn vendor_fasthttp_120_20() {
test_vendor_fasthttp_120("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn vendor_fasthttp_120_22() {
test_vendor_fasthttp_120("heroku/builder:22");
}

fn test_basic_http_122(builder: &str) {
fn test_basic_http_122() {
test_go_fixture(
"basic_http_122",
builder,
&[
"Detected Go version requirement: ~1.22.0",
"Installing go1.22.",
],
&[],
);
}
#[test]
#[ignore = "integration test"]
fn basic_http_122_20() {
test_basic_http_122("heroku/builder:20");
}
#[test]
#[ignore = "integration test"]
fn basic_http_122_22() {
test_basic_http_122("heroku/builder:22");
}

#[test]
#[ignore = "integration test"]
fn test_go_artifact_caching() {
TestRunner::default().build(
BuildConfig::new("heroku/builder:22", "tests/fixtures/basic_http_116"),
&IntegrationTestConfig::new("basic_http_116").into(),
|ctx| {
assert_contains!(
ctx.pack_stdout,
"Installing go1.16.15 (linux-amd64) from https://go.dev/dl/go1.16.15.linux-amd64.tar.gz",
);
assert_contains!(ctx.pack_stdout, "Installing go1.16.",);
let config = ctx.config.clone();
ctx.rebuild(config, |ctx| {
assert_contains!(ctx.pack_stdout, "Reusing go1.16.15 (linux-amd64)");
assert_contains!(ctx.pack_stdout, "Reusing go1.16.");
});
},
);
}

#[test]
#[ignore = "integration test"]
fn test_go_binary_arch() {
let integration_config = IntegrationTestConfig::new("basic_http_122");
let (contains, not_contain) = match integration_config.target.as_str() {
"aarch64-unknown-linux-musl" => (["(linux-arm64)", "linux-arm64.tar.gz"], "amd64"),
_ => (["(linux-amd64)", "linux-amd64.tar.gz"], "arm64"),
};

TestRunner::default().build(&integration_config.into(), |ctx| {
for contain in contains {
assert_contains!(ctx.pack_stdout, contain);
}
assert_not_contains!(ctx.pack_stdout, not_contain);
});
}

0 comments on commit e518444

Please sign in to comment.