Skip to content

Commit c7805b4

Browse files
committed
XXX hubris factory wip
1 parent 934da7d commit c7805b4

9 files changed

Lines changed: 175 additions & 25 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

factory/hubris/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ schemars = { workspace = true }
2121
sea-query = { workspace = true }
2222
serde = { workspace = true }
2323
serde_json = { workspace = true }
24-
serde_yaml = { workspace = true }
2524
slog = { workspace = true }
2625
slog-term = { workspace = true }
2726
smf = { workspace = true }
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version='1.0'?>
2+
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
3+
<service_bundle type='manifest' name='export'>
4+
<service name='site/buildomat/hubris-factory' type='service' version='0'>
5+
<create_default_instance enabled='false' />
6+
<dependency name='multi-user' grouping='require_all'
7+
restart_on='none' type='service'>
8+
<service_fmri value='svc:/milestone/multi-user'/>
9+
</dependency>
10+
11+
<exec_method name='start' type='method' timeout_seconds='60'
12+
exec='/opt/buildomat/hubris/lib/buildomat-factory-hubris -f /opt/buildomat/hubris/etc/config.toml -d /opt/buildomat/hubris/var/db.sqlite3' />
13+
14+
<exec_method name='stop' type='method' timeout_seconds='60'
15+
exec=':kill'/>
16+
17+
<property_group name='startd' type='framework'>
18+
<propval name='duration' type='astring' value='child'/>
19+
</property_group>
20+
21+
<stability value='Unstable'/>
22+
23+
<template>
24+
<common_name>
25+
<loctext xml:lang='C'>buildomat hubris factory</loctext>
26+
</common_name>
27+
<description>
28+
<loctext xml:lang='C'>buildomat hubris factory</loctext>
29+
</description>
30+
</template>
31+
32+
</service>
33+
</service_bundle>

factory/hubris/smf/hubris.xml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version='1.0'?>
2+
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
3+
<service_bundle type='manifest' name='export'>
4+
<service name='site/buildomat/hubris-agent' type='service' version='0'>
5+
<dependency name='multi-user' grouping='require_all'
6+
restart_on='none' type='service'>
7+
<service_fmri value='svc:/milestone/multi-user'/>
8+
</dependency>
9+
10+
<exec_method name='start' type='method' timeout_seconds='60'
11+
exec='/opt/buildomat/hubris/lib/start.sh'>
12+
<method_context>
13+
<method_credential user='build' group='build' />
14+
</method_context>
15+
</exec_method>
16+
<exec_method name='stop' type='method' timeout_seconds='60'
17+
exec=':kill'/>
18+
19+
<!--<property_group name='startd' type='framework'>
20+
<propval name='duration' type='astring' value='child'/>
21+
</property_group>-->
22+
23+
<stability value='Unstable'/>
24+
25+
<template>
26+
<common_name>
27+
<loctext xml:lang='C'>buildomat agent</loctext>
28+
</common_name>
29+
<description>
30+
<loctext xml:lang='C'>buildomat agent</loctext>
31+
</description>
32+
</template>
33+
34+
</service>
35+
</service_bundle>

factory/hubris/src/config.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) struct ConfigFile {
1717
//pub template: ConfigFileSlotTemplate,
1818
//#[serde(default)]
1919
pub target: HashMap<String, ConfigFileTarget>,
20+
pub slot: HashMap<String, ConfigFileSlot>,
2021
//pub slots: u32,
2122
//pub software_dir: String,
2223
pub nodename: String,
@@ -37,21 +38,29 @@ pub(crate) struct ConfigFileFactory {
3738
#[derive(Deserialize, Debug, Clone)]
3839
#[serde(deny_unknown_fields)]
3940
pub(crate) struct ConfigFileTarget {
40-
pub enable: bool,
41+
pub slot: String,
42+
}
43+
44+
#[derive(Deserialize, Debug, Clone)]
45+
#[serde(deny_unknown_fields)]
46+
pub(crate) struct ConfigFileSlot {
47+
pub vid: u16,
48+
pub pid: u16,
49+
pub serial: String,
50+
pub kind: Kind,
51+
}
52+
53+
#[derive(Deserialize, Debug, Clone)]
54+
#[serde(rename = "snake_case")]
55+
pub(crate) enum Kind {
56+
Basic,
57+
Rot,
4158
}
4259

4360
impl ConfigFile {
4461
pub fn for_instance(&self, id: &InstanceId) -> Result<InstanceInSlot> {
4562
Ok(InstanceInSlot { config: self, id: id.clone() })
4663
}
47-
48-
pub fn targets(&self) -> Vec<String> {
49-
self.target
50-
.iter()
51-
.filter(|(_, t)| t.enable)
52-
.map(|(id, _)| id.to_string())
53-
.collect()
54-
}
5564
}
5665

5766
pub(crate) struct InstanceInSlot<'a> {

factory/hubris/src/factory.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ async fn factory_task_one(log: &Logger, c: &Arc<Central>) -> Result<()> {
216216
let res = c
217217
.client
218218
.factory_lease()
219+
/*
220+
* XXX The supported target list should come from the slot manager, not
221+
* just the configuration.
222+
*/
219223
.body_map(|b| b.supported_targets(c.config.targets()))
220224
.send()
221225
.await?

factory/hubris/src/humility.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2024 Oxide Computer Company
3+
*/
4+
5+
use std::collections::BTreeMap;
6+
7+
use serde::Serialize;
8+
9+
/*
10+
* Humility accepts a JSON file describing a list of probes, so that they can be
11+
* selected by name using the -t (target) option. We write out such a file and
12+
* include the path in the the job environment (HUMILITY_ENVIRONMENT) so that it
13+
* can be used by humility commands invoked in the job.
14+
*/
15+
type EnvironmentFile = BTreeMap<String, Environment>;
16+
17+
#[derive(Serialize)]
18+
pub struct Environment {
19+
pub description: String,
20+
pub probe: String,
21+
pub archive: String,
22+
}
23+
24+
#[derive(Serialize)]
25+
#[serde(untagged)]
26+
pub enum Archive {
27+
Single(String),
28+
Slots(TwoSlots),
29+
}
30+
31+
#[derive(Serialize)]
32+
pub struct TwoSlots {
33+
imagea: String,
34+
imageb: String,
35+
}

factory/hubris/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod factory;
1515
mod unix;
1616
mod usb;
1717
mod workload;
18+
mod humility;
1819
//mod net;
1920
//mod nocloud;
2021
//mod propolis;

factory/hubris/src/workload.rs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use anyhow::{anyhow, bail, Result};
1818
use buildomat_common::OutputExt;
1919
use slog::{debug, error, info, o, warn, Logger};
2020

21-
const FMRI: &str = "svc:/site/buildomat/hubris-agent:default";
2221
const SERVICE: &str = "site/buildomat/hubris-agent";
2322

2423
pub(crate) async fn workload_worker(c: Arc<Central>) -> Result<()> {
@@ -56,7 +55,7 @@ async fn workload_worker_one(
5655
* service bundle.
5756
*/
5857
let svc = loc
59-
.get_service("site/buildomat/hubris-agent")?
58+
.get_service(SERVICE)?
6059
.ok_or_else(|| anyhow!("could not locate service {SERVICE:?}"))?;
6160

6261
/*
@@ -338,7 +337,7 @@ async fn instance_worker_one(
338337
let mut scf = smf::Scf::new()?;
339338
let loc = scf.scope_local()?;
340339
let svc = loc
341-
.get_service("site/buildomat/hubris-agent")?
340+
.get_service(SERVICE)?
342341
.ok_or_else(|| anyhow!("could not locate service {SERVICE:?}"))?;
343342

344343
match i.state {
@@ -352,7 +351,30 @@ async fn instance_worker_one(
352351
scrub_tmp(log, build_user)?;
353352

354353
/*
355-
* XXX Make sure we have an MCU-Link available.
354+
* XXX We need to confirm that power control is available for the
355+
* things that we intend to control here, and that the devices under
356+
* test begin in the OFF state.
357+
*/
358+
359+
/*
360+
* XXX Make sure we have hardware devices available.
361+
*
362+
* We need configuration here; looms, like in manufacturing,
363+
* to be able to specify a "gimlet", "sidecar", and "psc" target.
364+
*
365+
* We should select a loom here (which would include a list of
366+
* vid/pid/serial pairs for dongles) and commit to it for the
367+
* duration of this "instance".
368+
*
369+
* We should power up each device in sequence and ensure they are
370+
* flashed with stock images, confirm on some level that they are
371+
* working, and then power them back off.
372+
*
373+
* We need to be able to generate a humility environment file here
374+
* as part of the hand-off to the job.
375+
*
376+
* XXX Really this should actually happen before we've even promised
377+
* to start the job -- this should be part of the "slot" definition.
356378
*/
357379
let mculink =
358380
c.usb.mculink().ok_or_else(|| anyhow!("no MCU-Link found?"))?;
@@ -418,10 +440,9 @@ async fn instance_worker_one(
418440
InstanceState::Configured => {
419441
let mut scf = smf::Scf::new()?;
420442
let loc = scf.scope_local()?;
421-
let svc =
422-
loc.get_service("site/buildomat/hubris-agent")?.ok_or_else(
423-
|| anyhow!("could not locate service {SERVICE:?}"),
424-
)?;
443+
let svc = loc.get_service(SERVICE)?.ok_or_else(|| {
444+
anyhow!("could not locate service {SERVICE:?}")
445+
})?;
425446

426447
let smfi = svc.get_instance(&svcname)?.ok_or_else(|| {
427448
anyhow!("could not locate SMF instance {svcname:?}")
@@ -475,12 +496,14 @@ async fn instance_worker_one(
475496
* This is a terminal state and we can delete the
476497
* service instance.
477498
*/
499+
info!(log, "deleting SMF service");
500+
smfi.delete()?;
478501
}
479502
other => {
480503
/*
481504
* Try to disable the service.
482505
*/
483-
warn!(log, "SMF state: {other:?} --> disable!");
506+
info!(log, "disabling SMF service");
484507
smfi.disable(true)?;
485508
return Ok(DoNext::Sleep);
486509
}
@@ -490,13 +513,24 @@ async fn instance_worker_one(
490513
};
491514

492515
/*
493-
* XXX we need to ensure all processes owned by the builder
494-
* uid are killed... see: sigsend(2) / sigsendset(2)
516+
* While we have disabled the service, it is possible that some
517+
* processes could have been created outside the contract. Ensure
518+
* all processes for the build user are terminated and then remove
519+
* any files left behind.
495520
*/
496521
kill_all(log, build_user)?;
497522
dataset_destroy(log, build_user)?;
498523
scrub_tmp(log, build_user)?;
499524

525+
/*
526+
* XXX We need to confirm that we are able to shut off the power for
527+
* the devices under test here.
528+
*/
529+
530+
/*
531+
* XXX Change ownership of any USB devices back to root
532+
*/
533+
500534
c.db.instance_new_state(&id, InstanceState::Destroyed)?;
501535
Ok(DoNext::Immediate)
502536
}

0 commit comments

Comments
 (0)