Skip to content

Commit 9ee2e1f

Browse files
iximeowgjcolombo
andauthored
Use production propolis-server flags in PHD CI builds (#878)
We use the dev (e.g. non-release) build of propolis-server in PHD to get debug assertions, which we want, but we should use the `omicron-build` feature to be in line with production binaries. `omicron-build` had also acquired code related to failure injection, which we *also* want in CI so we can test migration failure codepaths. That is moved to a new `failure-injection` feature flag with a bit of ceremony to stop us early if we're ever inadvertently packaging a propolis-server with `omicron-build` and `failure-injection`. Fixes #412. --------- Co-authored-by: Greg Colombo <greg@oxidecomputer.com>
1 parent 6b5f2af commit 9ee2e1f

File tree

13 files changed

+106
-27
lines changed

13 files changed

+106
-27
lines changed

.github/buildomat/jobs/phd-build.sh

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ rustc --version
3030
# Build the Propolis server binary with 'dev' profile to enable assertions that
3131
# should fire during tests.
3232
banner build-propolis
33-
ptime -m cargo build --verbose -p propolis-server
33+
34+
# Compile propolis-server so that it allows development features to be used even
35+
# though the `omicron-build` feature is enabled.
36+
export PHD_BUILD="true"
37+
ptime -m cargo build --verbose -p propolis-server \
38+
--features omicron-build,failure-injection
3439

3540
# The PHD runner requires unwind-on-panic to catch certain test failures, so
3641
# build it with the 'dev' profile which is so configured.

.github/buildomat/phd-run-with-args.sh

+12
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ if [ ! -d "$tmpdir" ]; then
3737
mkdir $tmpdir
3838
fi
3939

40+
# We'll be using the reservoir, so set it to something higher than the default
41+
# 0MiB. Most tests would only need 512MiB (the default PHD guest VM memory
42+
# size), some tests want to run multiple VMs concurrently (particularly around
43+
# migration). 4096MiB is an arbitrary number intended to support the above and
44+
# that we might want to run those tests concurrently at some point.
45+
#
46+
# Currently the lab host these tests will run on is well-known and has much
47+
# more memory than this. Hopefully we won't have Propolis CI running on a
48+
# machine with ~4GiB of memory, but this number could be tuned down if the need
49+
# arises.
50+
pfexec /usr/lib/rsrvrctl -s 4096
51+
4052
banner 'Tests'
4153

4254
runner="$phddir/phd-runner"

bin/propolis-server/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ omicron-build = ["propolis/omicron-build"]
8383

8484
# Falcon builds require corresponding bits turned on in the dependency libs
8585
falcon = ["propolis/falcon"]
86+
87+
# Testing necessitates injecting failures which should hopefully be rare or even
88+
# never occur on real otherwise-unperturbed systems. We conditionally compile
89+
# code supporting failure injection to avoid the risk of somehow injecting
90+
# failures into a real system not under test.
91+
failure-injection = []

bin/propolis-server/src/lib/initializer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ impl MachineInitializer<'_> {
814814
Ok(())
815815
}
816816

817-
#[cfg(not(feature = "omicron-build"))]
817+
#[cfg(feature = "failure-injection")]
818818
pub fn initialize_test_devices(&mut self) {
819819
use propolis::hw::testdev::{
820820
MigrationFailureDevice, MigrationFailures,

bin/propolis-server/src/lib/migrate/preamble.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl Preamble {
5252
};
5353

5454
match comp {
55-
#[cfg(feature = "omicron-build")]
55+
#[cfg(not(feature = "failure-injection"))]
5656
ReplacementComponent::MigrationFailureInjector(_) => {
5757
return Err(MigrateError::InstanceSpecsIncompatible(
5858
format!(
@@ -62,7 +62,7 @@ impl Preamble {
6262
));
6363
}
6464

65-
#[cfg(not(feature = "omicron-build"))]
65+
#[cfg(feature = "failure-injection")]
6666
ReplacementComponent::MigrationFailureInjector(comp) => {
6767
let ComponentV0::MigrationFailureInjector(src) = to_amend
6868
else {

bin/propolis-server/src/lib/spec/api_spec_v0.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use super::{
2727
StorageDevice,
2828
};
2929

30-
#[cfg(not(feature = "omicron-build"))]
30+
#[cfg(feature = "failure-injection")]
3131
use super::MigrationFailure;
3232

3333
#[cfg(feature = "falcon")]
@@ -65,7 +65,7 @@ impl From<Spec> for InstanceSpecV0 {
6565
serial,
6666
pci_pci_bridges,
6767
pvpanic,
68-
#[cfg(not(feature = "omicron-build"))]
68+
#[cfg(feature = "failure-injection")]
6969
migration_failure,
7070
#[cfg(feature = "falcon")]
7171
softnpu,
@@ -157,7 +157,7 @@ impl From<Spec> for InstanceSpecV0 {
157157
);
158158
}
159159

160-
#[cfg(not(feature = "omicron-build"))]
160+
#[cfg(feature = "failure-injection")]
161161
if let Some(mig) = migration_failure {
162162
insert_component(
163163
&mut spec,
@@ -310,14 +310,14 @@ impl TryFrom<InstanceSpecV0> for Spec {
310310
// apply it to the builder later.
311311
boot_settings = Some((device_id, settings));
312312
}
313-
#[cfg(feature = "omicron-build")]
313+
#[cfg(not(feature = "failure-injection"))]
314314
ComponentV0::MigrationFailureInjector(_) => {
315315
return Err(ApiSpecError::FeatureCompiledOut {
316316
component: device_id,
317-
feature: "omicron-build",
317+
feature: "failure-injection",
318318
});
319319
}
320-
#[cfg(not(feature = "omicron-build"))]
320+
#[cfg(feature = "failure-injection")]
321321
ComponentV0::MigrationFailureInjector(mig) => {
322322
builder.add_migration_failure_device(MigrationFailure {
323323
id: device_id,

bin/propolis-server/src/lib/spec/builder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use super::{
2626
Board, BootOrderEntry, BootSettings, Disk, Nic, QemuPvpanic, SerialPort,
2727
};
2828

29-
#[cfg(not(feature = "omicron-build"))]
29+
#[cfg(feature = "failure-injection")]
3030
use super::MigrationFailure;
3131

3232
#[cfg(feature = "falcon")]
@@ -50,7 +50,7 @@ pub(crate) enum SpecBuilderError {
5050
#[error("pvpanic device already specified")]
5151
PvpanicInUse,
5252

53-
#[cfg(not(feature = "omicron-build"))]
53+
#[cfg(feature = "failure-injection")]
5454
#[error("migration failure injection already enabled")]
5555
MigrationFailureInjectionInUse,
5656

@@ -269,7 +269,7 @@ impl SpecBuilder {
269269
Ok(self)
270270
}
271271

272-
#[cfg(not(feature = "omicron-build"))]
272+
#[cfg(feature = "failure-injection")]
273273
pub fn add_migration_failure_device(
274274
&mut self,
275275
mig: MigrationFailure,

bin/propolis-server/src/lib/spec/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use propolis_api_types::instance_spec::{
3434
};
3535
use thiserror::Error;
3636

37-
#[cfg(not(feature = "omicron-build"))]
37+
#[cfg(feature = "failure-injection")]
3838
use propolis_api_types::instance_spec::components::devices::MigrationFailureInjector;
3939

4040
#[cfg(feature = "falcon")]
@@ -73,7 +73,7 @@ pub(crate) struct Spec {
7373
pub pci_pci_bridges: BTreeMap<SpecKey, PciPciBridge>,
7474
pub pvpanic: Option<QemuPvpanic>,
7575

76-
#[cfg(not(feature = "omicron-build"))]
76+
#[cfg(feature = "failure-injection")]
7777
pub migration_failure: Option<MigrationFailure>,
7878

7979
#[cfg(feature = "falcon")]
@@ -285,7 +285,7 @@ pub struct QemuPvpanic {
285285
pub spec: QemuPvpanicDesc,
286286
}
287287

288-
#[cfg(not(feature = "omicron-build"))]
288+
#[cfg(feature = "failure-injection")]
289289
#[derive(Clone, Debug)]
290290
pub struct MigrationFailure {
291291
pub id: SpecKey,

bin/propolis-server/src/lib/vm/ensure.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -564,10 +564,8 @@ async fn initialize_vm_objects(
564564
))?;
565565
init.initialize_network_devices(&chipset).await?;
566566

567-
#[cfg(not(feature = "omicron-build"))]
567+
#[cfg(feature = "failure-injection")]
568568
init.initialize_test_devices();
569-
#[cfg(feature = "omicron-build")]
570-
info!(log, "`omicron-build` feature enabled, ignoring any test devices");
571569

572570
#[cfg(feature = "falcon")]
573571
{

bin/propolis-server/src/main.rs

+13
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,19 @@ fn main() -> anyhow::Result<()> {
278278
// Ensure proper setup of USDT probes
279279
register_probes().unwrap();
280280

281+
#[cfg(all(
282+
feature = "omicron-build",
283+
any(feature = "failure-injection", feature = "falcon")
284+
))]
285+
if option_env!("PHD_BUILD") != Some("true") {
286+
panic!(
287+
"`omicron-build` is enabled alongside development features, \
288+
this build is NOT SUITABLE for production. Set PHD_BUILD=true in \
289+
the environment and rebuild propolis-server if you really need \
290+
this to work."
291+
);
292+
}
293+
281294
// Command line arguments.
282295
let args = Args::parse();
283296

crates/propolis-api-types/src/instance_spec/components/devices.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,8 @@ pub struct P9fs {
193193
/// Describes a synthetic device that registers for VM lifecycle notifications
194194
/// and returns errors during attempts to migrate.
195195
///
196-
/// This is only supported by Propolis servers compiled without the
197-
/// `omicron-build` feature.
196+
/// This is only supported by Propolis servers compiled with the
197+
/// `failure-injection` feature.
198198
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema)]
199199
#[serde(deny_unknown_fields)]
200200
pub struct MigrationFailureInjector {

openapi/propolis-server.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1526,7 +1526,7 @@
15261526
]
15271527
},
15281528
"MigrationFailureInjector": {
1529-
"description": "Describes a synthetic device that registers for VM lifecycle notifications and returns errors during attempts to migrate.\n\nThis is only supported by Propolis servers compiled without the `omicron-build` feature.",
1529+
"description": "Describes a synthetic device that registers for VM lifecycle notifications and returns errors during attempts to migrate.\n\nThis is only supported by Propolis servers compiled with the `failure-injection` feature.",
15301530
"type": "object",
15311531
"properties": {
15321532
"fail_exports": {

xtask/src/task_phd.rs

+50-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use anyhow::Context;
66
use camino::{Utf8Path, Utf8PathBuf};
7-
use std::{fs, process::Command, time};
7+
use std::{collections::HashMap, fs, process::Command, time};
88

99
macro_rules! cargo_log {
1010
($tag:literal, $($arg:tt)*) => {
@@ -112,6 +112,16 @@ struct PropolisArgs {
112112
/// If set, build `propolis-server` in release mode.
113113
#[clap(long, short = 'r')]
114114
release: bool,
115+
116+
/// If set, build `propolis-server` with `omicron-build`. This may enable
117+
/// codepaths that only work on `stlouis`, or otherwise expect specific test
118+
/// environment configuration (such as having a reservoir large enough to
119+
/// allocate VMs).
120+
///
121+
/// This can be helpful if you're debugging an issue that occurs in CI,
122+
/// where `propolis-server` is also tested with `omicron-build`.
123+
#[clap(long)]
124+
omicron_build: bool,
115125
}
116126

117127
#[derive(Debug, Clone, clap::Parser)]
@@ -270,15 +280,15 @@ impl Cmd {
270280
return Ok(());
271281
}
272282
Self::List { phd_args } => {
273-
let phd_runner = build_bin("phd-runner", false)?;
283+
let phd_runner = build_bin("phd-runner", false, None, None)?;
274284
let status = run_exit_code(
275285
phd_runner.command().arg("list").args(phd_args),
276286
)?;
277287
std::process::exit(status);
278288
}
279289

280290
Self::RunnerHelp { phd_args } => {
281-
let phd_runner = build_bin("phd-runner", false)?;
291+
let phd_runner = build_bin("phd-runner", false, None, None)?;
282292
let status = run_exit_code(
283293
phd_runner.command().arg("help").args(phd_args),
284294
)?;
@@ -292,7 +302,27 @@ impl Cmd {
292302
cmd
293303
}
294304
None => {
295-
let bin = build_bin("propolis-server", propolis_args.release)?;
305+
let mut server_build_env = HashMap::new();
306+
server_build_env
307+
.insert("PHD_BUILD".to_string(), "true".to_string());
308+
309+
// Some PHD tests specifically cover cases where a component in
310+
// the system has encountered an error, so enable
311+
// failure-injection. Do not enable `omicron-build` by default
312+
// because it configures `propolis-server` to expect an Omicron-
313+
// or stlouis-specific environment.
314+
let mut propolis_features = vec!["failure-injection"];
315+
if propolis_args.omicron_build {
316+
// If you know your environment looks like we'd expect in
317+
// Omicron, have at it!
318+
propolis_features.push("omicron-build");
319+
}
320+
let bin = build_bin(
321+
"propolis-server",
322+
propolis_args.release,
323+
Some(&propolis_features.join(",")),
324+
Some(server_build_env),
325+
)?;
296326
let path = bin
297327
.path()
298328
.try_into()
@@ -345,7 +375,7 @@ impl Cmd {
345375
anyhow::bail!("Missing artifacts config `{artifacts_toml}`!");
346376
}
347377

348-
let phd_runner = build_bin("phd-runner", false)?;
378+
let phd_runner = build_bin("phd-runner", false, None, None)?;
349379
let mut cmd = if cfg!(target_os = "illumos") {
350380
let mut cmd = Command::new("pfexec");
351381
cmd.arg(phd_runner.path());
@@ -418,15 +448,30 @@ impl BasePropolisArgs {
418448
}
419449
}
420450

451+
/// Build the binary `name` in debug or release with an optional build
452+
/// environment variables and a list of Cargo features.
453+
///
454+
/// `features` is passed directly to Cargo, and so must be a space or
455+
/// comma-separated list of features to activate.
421456
fn build_bin(
422457
name: impl AsRef<str>,
423458
release: bool,
459+
features: Option<&str>,
460+
build_env: Option<HashMap<String, String>>,
424461
) -> anyhow::Result<escargot::CargoRun> {
425462
let name = name.as_ref();
426463
cargo_log!("Compiling", "{name}");
427464

428465
let mut cmd =
429466
escargot::CargoBuild::new().package(name).bin(name).current_target();
467+
if let Some(features) = features {
468+
cmd = cmd.features(features);
469+
}
470+
if let Some(env) = build_env {
471+
for (k, v) in env {
472+
cmd = cmd.env(k, v);
473+
}
474+
}
430475
let profile = if release {
431476
cmd = cmd.release();
432477
"release [optimized]"

0 commit comments

Comments
 (0)