Skip to content

Commit 76b0af9

Browse files
committed
Merge branch 'affinity-db-model' into affinity-db-crud
2 parents 6ae1910 + bd95b03 commit 76b0af9

File tree

72 files changed

+2978
-1177
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+2978
-1177
lines changed

Cargo.lock

+38-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
members = [
33
"api_identity",
44
"bootstore",
5+
"brand-metadata",
56
"certificates",
67
"clickhouse-admin",
78
"clickhouse-admin/api",
@@ -41,6 +42,7 @@ members = [
4142
"dev-tools/openapi-manager",
4243
"dev-tools/openapi-manager/types",
4344
"dev-tools/oxlog",
45+
"dev-tools/pins",
4446
"dev-tools/reconfigurator-cli",
4547
"dev-tools/releng",
4648
"dev-tools/xtask",
@@ -135,6 +137,7 @@ members = [
135137
default-members = [
136138
"api_identity",
137139
"bootstore",
140+
"brand-metadata",
138141
"certificates",
139142
"clickhouse-admin",
140143
"clickhouse-admin/api",
@@ -175,6 +178,7 @@ default-members = [
175178
"dev-tools/openapi-manager",
176179
"dev-tools/openapi-manager/types",
177180
"dev-tools/oxlog",
181+
"dev-tools/pins",
178182
"dev-tools/reconfigurator-cli",
179183
"dev-tools/releng",
180184
# Do not include xtask in the list of default members, because this causes
@@ -493,6 +497,7 @@ nexus-types = { path = "nexus/types" }
493497
nom = "7.1.3"
494498
num-integer = "0.1.46"
495499
num = { version = "0.4.3", default-features = false, features = [ "libm" ] }
500+
omicron-brand-metadata = { path = "brand-metadata" }
496501
omicron-clickhouse-admin = { path = "clickhouse-admin" }
497502
omicron-certificates = { path = "certificates" }
498503
omicron-cockroach-admin = { path = "cockroach-admin" }
@@ -503,6 +508,7 @@ omicron-nexus = { path = "nexus" }
503508
omicron-omdb = { path = "dev-tools/omdb" }
504509
omicron-package = { path = "package" }
505510
omicron-passwords = { path = "passwords" }
511+
omicron-pins = { path = "dev-tools/pins" }
506512
omicron-rpaths = { path = "rpaths" }
507513
omicron-sled-agent = { path = "sled-agent" }
508514
omicron-test-utils = { path = "test-utils" }

brand-metadata/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "omicron-brand-metadata"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "MPL-2.0"
6+
7+
[dependencies]
8+
omicron-workspace-hack.workspace = true
9+
semver.workspace = true
10+
serde.workspace = true
11+
serde_json.workspace = true
12+
tar.workspace = true
13+
14+
[lints]
15+
workspace = true

brand-metadata/src/lib.rs

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
//! Handling of `oxide.json` metadata files in tarballs.
6+
//!
7+
//! `oxide.json` is originally defined by the omicron1(7) zone brand, which
8+
//! lives at <https://github.com/oxidecomputer/helios-omicron-brand>. tufaceous
9+
//! extended this format with additional archive types for identifying other
10+
//! types of tarballs; this crate covers those extensions so they can be used
11+
//! across the Omicron codebase.
12+
13+
use std::io::{Error, ErrorKind, Read, Result, Write};
14+
15+
use serde::{Deserialize, Serialize};
16+
17+
#[derive(Clone, Debug, Deserialize, Serialize)]
18+
pub struct Metadata {
19+
v: String,
20+
21+
// helios-build-utils defines a top-level `i` field for extra information,
22+
// but omicron-package doesn't use this for the package name and version.
23+
// We can also benefit from having rich types for these extra fields, so
24+
// any additional top-level fields (including `i`) that exist for a given
25+
// archive type should be deserialized as part of `ArchiveType`.
26+
#[serde(flatten)]
27+
t: ArchiveType,
28+
}
29+
30+
#[derive(Clone, Debug, Deserialize, Serialize)]
31+
#[serde(rename_all = "snake_case", tag = "t")]
32+
pub enum ArchiveType {
33+
// Originally defined in helios-build-utils (part of helios-omicron-brand):
34+
Baseline,
35+
Layer(LayerInfo),
36+
Os,
37+
38+
// tufaceous extensions:
39+
Rot,
40+
ControlPlane,
41+
}
42+
43+
#[derive(Clone, Debug, Deserialize, Serialize)]
44+
pub struct LayerInfo {
45+
pub pkg: String,
46+
pub version: semver::Version,
47+
}
48+
49+
impl Metadata {
50+
pub fn new(archive_type: ArchiveType) -> Metadata {
51+
Metadata { v: "1".into(), t: archive_type }
52+
}
53+
54+
pub fn append_to_tar<T: Write>(
55+
&self,
56+
a: &mut tar::Builder<T>,
57+
mtime: u64,
58+
) -> Result<()> {
59+
let mut b = serde_json::to_vec(self)?;
60+
b.push(b'\n');
61+
62+
let mut h = tar::Header::new_ustar();
63+
h.set_entry_type(tar::EntryType::Regular);
64+
h.set_username("root")?;
65+
h.set_uid(0);
66+
h.set_groupname("root")?;
67+
h.set_gid(0);
68+
h.set_path("oxide.json")?;
69+
h.set_mode(0o444);
70+
h.set_size(b.len().try_into().unwrap());
71+
h.set_mtime(mtime);
72+
h.set_cksum();
73+
74+
a.append(&h, b.as_slice())?;
75+
Ok(())
76+
}
77+
78+
/// Read `Metadata` from a tar archive.
79+
///
80+
/// `oxide.json` is generally the first file in the archive, so this should
81+
/// be a just-opened archive with no entries already read.
82+
pub fn read_from_tar<T: Read>(a: &mut tar::Archive<T>) -> Result<Metadata> {
83+
for entry in a.entries()? {
84+
let mut entry = entry?;
85+
if entry.path()? == std::path::Path::new("oxide.json") {
86+
return Ok(serde_json::from_reader(&mut entry)?);
87+
}
88+
}
89+
Err(Error::new(ErrorKind::InvalidData, "oxide.json is not present"))
90+
}
91+
92+
pub fn archive_type(&self) -> &ArchiveType {
93+
&self.t
94+
}
95+
96+
pub fn is_layer(&self) -> bool {
97+
matches!(&self.t, ArchiveType::Layer(_))
98+
}
99+
100+
pub fn layer_info(&self) -> Result<&LayerInfo> {
101+
match &self.t {
102+
ArchiveType::Layer(info) => Ok(info),
103+
_ => Err(Error::new(
104+
ErrorKind::InvalidData,
105+
"archive is not the \"layer\" type",
106+
)),
107+
}
108+
}
109+
110+
pub fn is_baseline(&self) -> bool {
111+
matches!(&self.t, ArchiveType::Baseline)
112+
}
113+
114+
pub fn is_os(&self) -> bool {
115+
matches!(&self.t, ArchiveType::Os)
116+
}
117+
118+
pub fn is_rot(&self) -> bool {
119+
matches!(&self.t, ArchiveType::Rot)
120+
}
121+
122+
pub fn is_control_plane(&self) -> bool {
123+
matches!(&self.t, ArchiveType::ControlPlane)
124+
}
125+
}
126+
127+
#[cfg(test)]
128+
mod tests {
129+
use super::*;
130+
131+
#[test]
132+
fn test_deserialize() {
133+
let metadata: Metadata = serde_json::from_str(
134+
r#"{"v":"1","t":"layer","pkg":"nexus","version":"12.0.0-0.ci+git3a2ed5e97b3"}"#,
135+
)
136+
.unwrap();
137+
assert!(metadata.is_layer());
138+
let info = metadata.layer_info().unwrap();
139+
assert_eq!(info.pkg, "nexus");
140+
assert_eq!(info.version, "12.0.0-0.ci+git3a2ed5e97b3".parse().unwrap());
141+
142+
let metadata: Metadata = serde_json::from_str(
143+
r#"{"v":"1","t":"os","i":{"checksum":"42eda100ee0e3bf44b9d0bb6a836046fa3133c378cd9d3a4ba338c3ba9e56eb7","name":"ci 3a2ed5e/9d37813 2024-12-20 08:54"}}"#,
144+
).unwrap();
145+
assert!(metadata.is_os());
146+
147+
let metadata: Metadata =
148+
serde_json::from_str(r#"{"v":"1","t":"control_plane"}"#).unwrap();
149+
assert!(metadata.is_control_plane());
150+
}
151+
}

clickhouse-admin/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ clap.workspace = true
1212
clickhouse-admin-api.workspace = true
1313
clickhouse-admin-types.workspace = true
1414
dropshot.workspace = true
15+
flume = {workspace = true, features = ["async"]}
1516
http.workspace = true
1617
illumos-utils.workspace = true
1718
omicron-common.workspace = true

0 commit comments

Comments
 (0)