Add an rpmostree-client sub-crate

This is intended to be published to https://crates.io/crates/rpmostree-client
Part of https://github.com/coreos/rpm-ostree/issues/2389

This directly imports the code from
5551c54c6e/tests/inst/src/rpmostree.rs

Once merged and released I'll try converting the ostree test suite
over as well as Zincati.

Internally add a testutils helper to validate it works.
This commit is contained in:
Colin Walters 2021-02-12 18:47:36 +00:00 committed by OpenShift Merge Robot
parent 7bf885ecf7
commit e6c045cada
8 changed files with 537 additions and 1 deletions

11
Cargo.lock generated
View File

@ -1197,6 +1197,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "rpmostree-client"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "rpmostree-rust"
version = "0.1.0"
@ -1228,6 +1238,7 @@ dependencies = [
"phf",
"rand 0.8.3",
"rayon",
"rpmostree-client",
"rust-ini",
"serde",
"serde_derive",

View File

@ -60,6 +60,7 @@ libdnf-sys = { path = "rust/libdnf-sys", version = "0.1.0" }
memfd = "0.3.0"
rust-ini = "0.16.1"
os-release = "0.1.0"
rpmostree-client = { path = "rust/rpmostree-client", version = "0.1.0" }
[build-dependencies]
cbindgen = "0.16.0"

View File

@ -0,0 +1,16 @@
[package]
name = "rpmostree-client"
description = "Client side bindings for rpm-ostree"
version = "0.1.0"
edition = "2018"
license = "Apache-2.0"
keywords = ["ostree", "rpm-ostree"]
documentation = "http://docs.rs/rpmostree-client"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.38"
serde = { version = "1.0.123", features = ["derive"] }
serde_derive = "1.0.118"
serde_json = "1.0.62"

View File

@ -0,0 +1,64 @@
//! APIs for interacting with rpm-ostree client side.
use anyhow::Context;
use serde_derive::Deserialize;
use std::process::Command;
/// Our generic catchall fatal error, expected to be converted
/// to a string to output to a terminal or logs.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync + 'static>>;
/// Representation of the rpm-ostree client-side state; this
/// can be parsed directly from the output of `rpm-ostree status --json`.
/// Currently not all fields are here, but that is a bug.
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Status {
pub deployments: Vec<Deployment>,
}
/// A single deployment, i.e. a bootable ostree commit
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Deployment {
pub unlocked: Option<String>,
pub osname: String,
pub pinned: bool,
pub checksum: String,
pub staged: Option<bool>,
pub booted: bool,
pub serial: u32,
pub origin: String,
}
/// Gather a snapshot of the system status.
pub fn query_status() -> Result<Status> {
// Retry on temporary activation failures, see
// https://github.com/coreos/rpm-ostree/issues/2531
let pause = std::time::Duration::from_secs(1);
let max_retries = 10;
let mut retries = 0;
let cmd_res = loop {
retries += 1;
let res = Command::new("rpm-ostree")
.args(&["status", "--json"])
.output()
.context("failed to spawn 'rpm-ostree status'")?;
if res.status.success() || retries >= max_retries {
break res;
}
std::thread::sleep(pause);
};
if !cmd_res.status.success() {
return Err(format!(
"running 'rpm-ostree status' failed: {}",
String::from_utf8_lossy(&cmd_res.stderr)
)
.into());
}
Ok(serde_json::from_slice(&cmd_res.stdout)
.context("failed to parse 'rpm-ostree status' output")?)
}

View File

@ -0,0 +1,417 @@
{
"deployments": [
{
"unlocked": "none",
"base-commit-meta": {
"coreos-assembler.config-gitrev": "80966f951c766846da070b4c168b9170c61513e2",
"coreos-assembler.config-dirty": "false",
"rpmostree.inputhash": "06539cc4a4265eec2045a349fe80de451a61628c1b117e171d80663d3e3f42eb",
"coreos-assembler.basearch": "x86_64",
"version": "33.21",
"rpmostree.initramfs-args": [
"--add=ignition",
"--no-hostonly",
"--omit=nfs",
"--omit=lvm",
"--omit=iscsi"
],
"rpmostree.rpmmd-repos": [
{
"id": "fedora-coreos-pool",
"timestamp": 1053029086517002240
},
{
"id": "fedora",
"timestamp": -2945197617627267072
},
{
"id": "fedora-updates",
"timestamp": -389530169125109760
}
]
},
"requested-local-packages": [],
"base-removals": [],
"gpg-enabled": false,
"origin": "fedora/33/x86_64/silverblue",
"osname": "fedora-silverblue",
"pinned": false,
"requested-base-local-replacements": [
"rpm-ostree-2021.1-2.fc33.x86_64",
"rpm-ostree-libs-2021.1-2.fc33.x86_64"
],
"checksum": "63335a77f9853618ba1a5f139c5805e82176a2a040ef5e34d7402e12263af5bb",
"regenerate-initramfs": false,
"id": "fedora-silverblue-63335a77f9853618ba1a5f139c5805e82176a2a040ef5e34d7402e12263af5bb.0",
"version": "33.21",
"base-version": "33.21",
"requested-base-removals": [],
"base-checksum": "229387d3c0bb8ad698228ca5702eca72aed8b298a7c800be1dc72bab160a9f7f",
"requested-packages": [
"xsel",
"gdb",
"ykclient",
"krb5-workstation",
"ykpers",
"git-evtag",
"fish",
"qemu-system-aarch64",
"strace",
"qemu-kvm",
"virt-manager",
"opensc",
"tmux",
"pcsc-lite-ccid",
"tilix",
"libvirt"
],
"base-timestamp": 1612554510,
"serial": 0,
"layered-commit-meta": {
"rpmostree.clientlayer": true,
"rpmostree.removed-base-packages": [],
"version": "33.21",
"rpmostree.packages": [
"fish",
"gdb",
"git-evtag",
"krb5-workstation",
"libvirt",
"opensc",
"pcsc-lite-ccid",
"qemu-kvm",
"qemu-system-aarch64",
"strace",
"tilix",
"tmux",
"virt-manager",
"xsel",
"ykclient",
"ykpers"
],
"rpmostree.clientlayer_version": 4,
"rpmostree.replaced-base-packages": [
[
[
"rpm-ostree-2021.1-2.fc33.x86_64",
"rpm-ostree",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-2021.1-3.fc33.x86_64",
"rpm-ostree",
0,
"2021.1",
"3.fc33",
"x86_64"
]
],
[
[
"rpm-ostree-libs-2021.1-2.fc33.x86_64",
"rpm-ostree-libs",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-libs-2021.1-3.fc33.x86_64",
"rpm-ostree-libs",
0,
"2021.1",
"3.fc33",
"x86_64"
]
]
],
"rpmostree.state-sha512": "8b037fba282e3773ef17d4c396ee958765c01e85c7a6a29ec9df1bb2213022cf599da15ec4df982c4f0904012b165c4370a9f14b12c48d0684a66928c4f34b34",
"rpmostree.rpmmd-repos": [
{
"id": "fedora-cisco-openh264",
"timestamp": 1598382634
},
{
"id": "updates",
"timestamp": 1612486906
},
{
"id": "fedora",
"timestamp": 1603150039
}
]
},
"base-local-replacements": [
[
[
"rpm-ostree-2021.1-2.fc33.x86_64",
"rpm-ostree",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-2021.1-3.fc33.x86_64",
"rpm-ostree",
0,
"2021.1",
"3.fc33",
"x86_64"
]
],
[
[
"rpm-ostree-libs-2021.1-2.fc33.x86_64",
"rpm-ostree-libs",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-libs-2021.1-3.fc33.x86_64",
"rpm-ostree-libs",
0,
"2021.1",
"3.fc33",
"x86_64"
]
]
],
"timestamp": 1612555369,
"packages": [
"fish",
"gdb",
"git-evtag",
"krb5-workstation",
"libvirt",
"opensc",
"pcsc-lite-ccid",
"qemu-kvm",
"qemu-system-aarch64",
"strace",
"tilix",
"tmux",
"virt-manager",
"xsel",
"ykclient",
"ykpers"
],
"booted": true,
"initramfs-etc": []
},
{
"unlocked": "none",
"pending-base-version": "33.21",
"base-commit-meta": {
"coreos-assembler.config-gitrev": "bbd5282b507c5b29e3a5f12e9da21f3aaa0f0e00",
"coreos-assembler.config-dirty": "false",
"rpmostree.inputhash": "f33469d0f6c5d5ce5e30345fa5b002a8e4ebf5ea397caad000bdc32cd74897a6",
"coreos-assembler.basearch": "x86_64",
"version": "33.17",
"rpmostree.initramfs-args": [
"--add=ignition",
"--no-hostonly",
"--omit=nfs",
"--omit=lvm",
"--omit=iscsi"
],
"rpmostree.rpmmd-repos": [
{
"id": "fedora-coreos-pool",
"timestamp": 7926905303512121344
},
{
"id": "fedora",
"timestamp": -2945197617627267072
},
{
"id": "fedora-updates",
"timestamp": -6611277243593261056
}
]
},
"requested-local-packages": [],
"base-removals": [],
"gpg-enabled": false,
"osname": "fedora-silverblue",
"origin": "fedora/33/x86_64/silverblue",
"packages": [
"fish",
"gdb",
"git-evtag",
"krb5-workstation",
"libvirt",
"opensc",
"pcsc-lite-ccid",
"qemu-kvm",
"qemu-system-aarch64",
"strace",
"tilix",
"tmux",
"virt-manager",
"xsel",
"ykclient",
"ykpers"
],
"pinned": false,
"requested-base-local-replacements": [
"rpm-ostree-2021.1-2.fc33.x86_64",
"rpm-ostree-libs-2021.1-2.fc33.x86_64"
],
"checksum": "775d54e89bc74731ec27db04f12510c0269c8cbab3ad5e39e0a4d693231ef072",
"regenerate-initramfs": false,
"id": "fedora-silverblue-775d54e89bc74731ec27db04f12510c0269c8cbab3ad5e39e0a4d693231ef072.0",
"version": "33.17",
"base-version": "33.17",
"base-checksum": "deea0555cb7d3eb042df9a85d4efcbb9f70d778a9a9557715c0e398978233cd7",
"requested-base-removals": [],
"requested-packages": [
"xsel",
"gdb",
"ykclient",
"krb5-workstation",
"ykpers",
"git-evtag",
"fish",
"qemu-system-aarch64",
"strace",
"qemu-kvm",
"virt-manager",
"opensc",
"tmux",
"pcsc-lite-ccid",
"tilix",
"libvirt"
],
"base-timestamp": 1611079148,
"serial": 0,
"layered-commit-meta": {
"rpmostree.clientlayer": true,
"rpmostree.removed-base-packages": [],
"version": "33.17",
"rpmostree.packages": [
"fish",
"gdb",
"git-evtag",
"krb5-workstation",
"libvirt",
"opensc",
"pcsc-lite-ccid",
"qemu-kvm",
"qemu-system-aarch64",
"strace",
"tilix",
"tmux",
"virt-manager",
"xsel",
"ykclient",
"ykpers"
],
"rpmostree.clientlayer_version": 4,
"rpmostree.replaced-base-packages": [
[
[
"rpm-ostree-2021.1-2.fc33.x86_64",
"rpm-ostree",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-2020.10-1.fc33.x86_64",
"rpm-ostree",
0,
"2020.10",
"1.fc33",
"x86_64"
]
],
[
[
"rpm-ostree-libs-2021.1-2.fc33.x86_64",
"rpm-ostree-libs",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-libs-2020.10-1.fc33.x86_64",
"rpm-ostree-libs",
0,
"2020.10",
"1.fc33",
"x86_64"
]
]
],
"rpmostree.state-sha512": "684f72c2b63379ee17a8f3055ccdfb3d54d255ed5bf1965788be21e804a0aff9e08620519dacaa34cc8cbad038474e8b0abbc68ee98988c547ad599f93ddcfa1",
"rpmostree.rpmmd-repos": [
{
"id": "fedora-cisco-openh264",
"timestamp": 1598382634
},
{
"id": "updates",
"timestamp": 1611022500
},
{
"id": "fedora",
"timestamp": 1603150039
}
]
},
"base-local-replacements": [
[
[
"rpm-ostree-2021.1-2.fc33.x86_64",
"rpm-ostree",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-2020.10-1.fc33.x86_64",
"rpm-ostree",
0,
"2020.10",
"1.fc33",
"x86_64"
]
],
[
[
"rpm-ostree-libs-2021.1-2.fc33.x86_64",
"rpm-ostree-libs",
0,
"2021.1",
"2.fc33",
"x86_64"
],
[
"rpm-ostree-libs-2020.10-1.fc33.x86_64",
"rpm-ostree-libs",
0,
"2020.10",
"1.fc33",
"x86_64"
]
]
],
"timestamp": 1611081986,
"pending-base-timestamp": 1612554510,
"booted": false,
"pending-base-checksum": "229387d3c0bb8ad698228ca5702eca72aed8b298a7c800be1dc72bab160a9f7f",
"initramfs-etc": []
}
],
"transaction": null,
"cached-update": null
}

View File

@ -0,0 +1,10 @@
use anyhow::Result;
use rpmostree_client;
#[test]
fn parse_workstation() -> Result<()> {
let data = include_str!("fixtures/workstation-status.json");
let state: rpmostree_client::Status = serde_json::from_str(data)?;
assert_eq!(state.deployments.len(), 2);
Ok(())
}

View File

@ -45,6 +45,8 @@ struct SyntheticUpgradeOpts {
enum Opt {
/// Generate an OS update by changing ELF files
GenerateSyntheticUpgrade(SyntheticUpgradeOpts),
/// Validate that we can parse the output of `rpm-ostree status --json`.
ValidateParseStatus,
}
/// Returns `true` if a file is ELF; see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
@ -211,10 +213,24 @@ fn update_os_tree(opts: &SyntheticUpgradeOpts) -> Result<()> {
Ok(())
}
// We always expect to be in a booted deployment. The real goal here
// is to ensure that everything output from status --json in our
// test suite can be parsed by our client side bindings.
//
// In the future we'll switch the client API to have like
// query_status_deny_unknown_fields() which will force us
// to update the client bindings when adding new fields.
fn validate_parse_status() -> Result<()> {
let s = rpmostree_client::query_status().map_err(anyhow::Error::msg)?;
assert_ne!(s.deployments.len(), 0);
Ok(())
}
pub(crate) fn testutils_entrypoint(args: Vec<String>) -> CxxResult<()> {
let opt = Opt::from_iter(args.iter());
match opt {
Opt::GenerateSyntheticUpgrade(ref opts) => update_os_tree(opts)?,
Opt::ValidateParseStatus => validate_parse_status()?,
};
Ok(())
}

View File

@ -1,5 +1,5 @@
#!/bin/bash
set -euo pipefail
set -xeuo pipefail
. ${KOLA_EXT_DATA}/libtest.sh
cd $(mktemp -d)
@ -15,6 +15,7 @@ assert_jq status.json \
'.deployments[0]["requested-base-removals"]' \
'.deployments[0]["layered-commit-meta"]|not'
rm status.json
rpm-ostree testutils validate-parse-status
echo "ok empty pkg arrays, and commit meta correct in status json"
# Ensure we return an error when passing a wrong option.