Skip to content

Commit

Permalink
Merge branch 'affinity-db-model' into affinity-db-crud
Browse files Browse the repository at this point in the history
  • Loading branch information
smklein committed Feb 4, 2025
2 parents 6ae1910 + bd95b03 commit 76b0af9
Show file tree
Hide file tree
Showing 72 changed files with 2,978 additions and 1,177 deletions.
42 changes: 38 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"api_identity",
"bootstore",
"brand-metadata",
"certificates",
"clickhouse-admin",
"clickhouse-admin/api",
Expand Down Expand Up @@ -41,6 +42,7 @@ members = [
"dev-tools/openapi-manager",
"dev-tools/openapi-manager/types",
"dev-tools/oxlog",
"dev-tools/pins",
"dev-tools/reconfigurator-cli",
"dev-tools/releng",
"dev-tools/xtask",
Expand Down Expand Up @@ -135,6 +137,7 @@ members = [
default-members = [
"api_identity",
"bootstore",
"brand-metadata",
"certificates",
"clickhouse-admin",
"clickhouse-admin/api",
Expand Down Expand Up @@ -175,6 +178,7 @@ default-members = [
"dev-tools/openapi-manager",
"dev-tools/openapi-manager/types",
"dev-tools/oxlog",
"dev-tools/pins",
"dev-tools/reconfigurator-cli",
"dev-tools/releng",
# Do not include xtask in the list of default members, because this causes
Expand Down Expand Up @@ -493,6 +497,7 @@ nexus-types = { path = "nexus/types" }
nom = "7.1.3"
num-integer = "0.1.46"
num = { version = "0.4.3", default-features = false, features = [ "libm" ] }
omicron-brand-metadata = { path = "brand-metadata" }
omicron-clickhouse-admin = { path = "clickhouse-admin" }
omicron-certificates = { path = "certificates" }
omicron-cockroach-admin = { path = "cockroach-admin" }
Expand All @@ -503,6 +508,7 @@ omicron-nexus = { path = "nexus" }
omicron-omdb = { path = "dev-tools/omdb" }
omicron-package = { path = "package" }
omicron-passwords = { path = "passwords" }
omicron-pins = { path = "dev-tools/pins" }
omicron-rpaths = { path = "rpaths" }
omicron-sled-agent = { path = "sled-agent" }
omicron-test-utils = { path = "test-utils" }
Expand Down
15 changes: 15 additions & 0 deletions brand-metadata/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "omicron-brand-metadata"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"

[dependencies]
omicron-workspace-hack.workspace = true
semver.workspace = true
serde.workspace = true
serde_json.workspace = true
tar.workspace = true

[lints]
workspace = true
151 changes: 151 additions & 0 deletions brand-metadata/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Handling of `oxide.json` metadata files in tarballs.
//!
//! `oxide.json` is originally defined by the omicron1(7) zone brand, which
//! lives at <https://github.com/oxidecomputer/helios-omicron-brand>. tufaceous
//! extended this format with additional archive types for identifying other
//! types of tarballs; this crate covers those extensions so they can be used
//! across the Omicron codebase.
use std::io::{Error, ErrorKind, Read, Result, Write};

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Metadata {
v: String,

// helios-build-utils defines a top-level `i` field for extra information,
// but omicron-package doesn't use this for the package name and version.
// We can also benefit from having rich types for these extra fields, so
// any additional top-level fields (including `i`) that exist for a given
// archive type should be deserialized as part of `ArchiveType`.
#[serde(flatten)]
t: ArchiveType,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case", tag = "t")]
pub enum ArchiveType {
// Originally defined in helios-build-utils (part of helios-omicron-brand):
Baseline,
Layer(LayerInfo),
Os,

// tufaceous extensions:
Rot,
ControlPlane,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LayerInfo {
pub pkg: String,
pub version: semver::Version,
}

impl Metadata {
pub fn new(archive_type: ArchiveType) -> Metadata {
Metadata { v: "1".into(), t: archive_type }
}

pub fn append_to_tar<T: Write>(
&self,
a: &mut tar::Builder<T>,
mtime: u64,
) -> Result<()> {
let mut b = serde_json::to_vec(self)?;
b.push(b'\n');

let mut h = tar::Header::new_ustar();
h.set_entry_type(tar::EntryType::Regular);
h.set_username("root")?;
h.set_uid(0);
h.set_groupname("root")?;
h.set_gid(0);
h.set_path("oxide.json")?;
h.set_mode(0o444);
h.set_size(b.len().try_into().unwrap());
h.set_mtime(mtime);
h.set_cksum();

a.append(&h, b.as_slice())?;
Ok(())
}

/// Read `Metadata` from a tar archive.
///
/// `oxide.json` is generally the first file in the archive, so this should
/// be a just-opened archive with no entries already read.
pub fn read_from_tar<T: Read>(a: &mut tar::Archive<T>) -> Result<Metadata> {
for entry in a.entries()? {
let mut entry = entry?;
if entry.path()? == std::path::Path::new("oxide.json") {
return Ok(serde_json::from_reader(&mut entry)?);
}
}
Err(Error::new(ErrorKind::InvalidData, "oxide.json is not present"))
}

pub fn archive_type(&self) -> &ArchiveType {
&self.t
}

pub fn is_layer(&self) -> bool {
matches!(&self.t, ArchiveType::Layer(_))
}

pub fn layer_info(&self) -> Result<&LayerInfo> {
match &self.t {
ArchiveType::Layer(info) => Ok(info),
_ => Err(Error::new(
ErrorKind::InvalidData,
"archive is not the \"layer\" type",
)),
}
}

pub fn is_baseline(&self) -> bool {
matches!(&self.t, ArchiveType::Baseline)
}

pub fn is_os(&self) -> bool {
matches!(&self.t, ArchiveType::Os)
}

pub fn is_rot(&self) -> bool {
matches!(&self.t, ArchiveType::Rot)
}

pub fn is_control_plane(&self) -> bool {
matches!(&self.t, ArchiveType::ControlPlane)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_deserialize() {
let metadata: Metadata = serde_json::from_str(
r#"{"v":"1","t":"layer","pkg":"nexus","version":"12.0.0-0.ci+git3a2ed5e97b3"}"#,
)
.unwrap();
assert!(metadata.is_layer());
let info = metadata.layer_info().unwrap();
assert_eq!(info.pkg, "nexus");
assert_eq!(info.version, "12.0.0-0.ci+git3a2ed5e97b3".parse().unwrap());

let metadata: Metadata = serde_json::from_str(
r#"{"v":"1","t":"os","i":{"checksum":"42eda100ee0e3bf44b9d0bb6a836046fa3133c378cd9d3a4ba338c3ba9e56eb7","name":"ci 3a2ed5e/9d37813 2024-12-20 08:54"}}"#,
).unwrap();
assert!(metadata.is_os());

let metadata: Metadata =
serde_json::from_str(r#"{"v":"1","t":"control_plane"}"#).unwrap();
assert!(metadata.is_control_plane());
}
}
1 change: 1 addition & 0 deletions clickhouse-admin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ clap.workspace = true
clickhouse-admin-api.workspace = true
clickhouse-admin-types.workspace = true
dropshot.workspace = true
flume = {workspace = true, features = ["async"]}
http.workspace = true
illumos-utils.workspace = true
omicron-common.workspace = true
Expand Down
Loading

0 comments on commit 76b0af9

Please sign in to comment.