difftreelog
feat usbd
24 files changed
Cargo.lockdiffbeforeafterboth1912version = "0.2.0"1912version = "0.2.0"1913dependencies = [1913dependencies = [1914 "anyhow",1914 "anyhow",1915 "base64 0.22.1",1915 "camino",1916 "camino",1916 "clap",1917 "clap",1917 "clap_complete",1918 "clap_complete",1918 "fleet-base",1919 "fleet-base",1920 "fleet-usb",1919 "futures",1921 "futures",1920 "goodlog-subscriber",1922 "goodlog-subscriber",1921 "itertools 0.15.0",1923 "itertools 0.15.0",1924 "nix",1922 "nix-eval",1925 "nix-eval",1926 "rand 0.10.1",1923 "remowt-fleet",1927 "remowt-fleet",1924 "serde",1928 "serde",1925 "serde_json",1929 "serde_json",1928 "tempfile",1932 "tempfile",1929 "tokio",1933 "tokio",1930 "tracing",1934 "tracing",1935 "zbus",1936 "zstd",1931]1937]193219381933[[package]]1939[[package]]2005 "unicode_categories",2011 "unicode_categories",2006]2012]20132014[[package]]2015name = "fleet-usb"2016version = "0.1.9"2017dependencies = [2018 "age",2019 "anyhow",2020 "base64 0.22.1",2021 "camino",2022 "chacha20poly1305",2023 "hex",2024 "hmac 0.13.0",2025 "serde",2026 "serde_json",2027 "sha2 0.11.0",2028]20292030[[package]]2031name = "fleet-usbd"2032version = "0.1.9"2033dependencies = [2034 "age",2035 "anyhow",2036 "base64 0.22.1",2037 "bytes",2038 "camino",2039 "clap",2040 "fleet-usb",2041 "futures",2042 "goodlog-subscriber",2043 "hex",2044 "hostname",2045 "http-body-util",2046 "hyper",2047 "hyper-util",2048 "nix",2049 "nix-eval",2050 "rand 0.10.1",2051 "tokio",2052 "tracing",2053]200720542008[[package]]2055[[package]]2009name = "fluent"2056name = "fluent"7840source = "registry+https://github.com/rust-lang/crates.io-index"7887source = "registry+https://github.com/rust-lang/crates.io-index"7841checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"7888checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"78897890[[package]]7891name = "zstd"7892version = "0.13.3"7893source = "registry+https://github.com/rust-lang/crates.io-index"7894checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"7895dependencies = [7896 "zstd-safe",7897]78987899[[package]]7900name = "zstd-safe"7901version = "7.2.4"7902source = "registry+https://github.com/rust-lang/crates.io-index"7903checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"7904dependencies = [7905 "zstd-sys",7906]79077908[[package]]7909name = "zstd-sys"7910version = "2.0.16+zstd.1.5.7"7911source = "registry+https://github.com/rust-lang/crates.io-index"7912checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"7913dependencies = [7914 "cc",7915 "pkg-config",7916]784279177843[[package]]7918[[package]]7844name = "zvariant"7919name = "zvariant"Cargo.tomldiffbeforeafterboth9[workspace.dependencies]9[workspace.dependencies]10fleet-base = { path = "./crates/fleet-base" }10fleet-base = { path = "./crates/fleet-base" }11fleet-shared = { path = "./crates/fleet-shared" }11fleet-shared = { path = "./crates/fleet-shared" }12fleet-usb = { path = "./crates/fleet-usb" }12nix-eval = { path = "./crates/nix-eval" }13nix-eval = { path = "./crates/nix-eval" }13nixlike = { path = "./crates/nixlike" }14nixlike = { path = "./crates/nixlike" }14opentelemetry-exporter-env = { path = "./crates/opentelemetry-exporter-env" }15opentelemetry-exporter-env = { path = "./crates/opentelemetry-exporter-env" }36bindgen = "0.72.0"37bindgen = "0.72.0"37bytes = "1.11.0"38bytes = "1.11.0"38camino = "1.2.2"39camino = "1.2.2"40chacha20poly1305 = "0.10"39chrono = { version = "0.4.41", features = ["serde"] }41chrono = { version = "0.4.41", features = ["serde"] }40clap = { version = "4.5", features = ["derive", "env", "unicode", "wrap_help"] }42clap = { version = "4.5", features = ["derive", "env", "unicode", "wrap_help"] }41clap_complete = "4.5"43clap_complete = "4.5"47hex = "0.4.3"49hex = "0.4.3"48hmac = "0.13.0"50hmac = "0.13.0"49hostname = "0.4.1"51hostname = "0.4.1"52http-body-util = "0.1"53hyper = { version = "1", features = ["http1", "server"] }54hyper-util = { version = "0.1", features = ["tokio"] }50human-repr = "1.1"55human-repr = "1.1"51indicatif = "0.18"56indicatif = "0.18"52indoc = "2.0.6"57indoc = "2.0.6"85unicode_categories = "0.1.1"90unicode_categories = "0.1.1"86vte = { version = "0.15.0", features = ["ansi"] }91vte = { version = "0.15.0", features = ["ansi"] }87x25519-dalek = { version = "2.0.1", features = ["getrandom"] }92x25519-dalek = { version = "2.0.1", features = ["getrandom"] }93zstd = "0.13"88zbus = "5.16.0"94zbus = "5.16.0"89zbus_polkit = "5.0.0"95zbus_polkit = "5.0.0"90goodlog-subscriber = { version = "0.1.9", path = "crates/goodlog-subscriber" }96goodlog-subscriber = { version = "0.1.9", path = "crates/goodlog-subscriber" }cmds/fleet/Cargo.tomldiffbeforeafterboth9910[dependencies]10[dependencies]11anyhow.workspace = true11anyhow.workspace = true12base64.workspace = true12camino.workspace = true13camino.workspace = true13clap.workspace = true14clap.workspace = true14clap_complete.workspace = true15clap_complete.workspace = true15fleet-base.workspace = true16fleet-base.workspace = true17fleet-usb.workspace = true18nix.workspace = true16nix-eval.workspace = true19nix-eval.workspace = true20rand.workspace = true17remowt-fleet.workspace = true21remowt-fleet.workspace = true18serde.workspace = true22serde.workspace = true19serde_json.workspace = true23serde_json.workspace = true20tempfile.workspace = true24tempfile.workspace = true21tokio.workspace = true25tokio.workspace = true22tracing.workspace = true26tracing.workspace = true27zbus.workspace = true28zstd.workspace = true232924futures.workspace = true30futures.workspace = true25itertools.workspace = true31itertools.workspace = truecmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth33 build_attr: String,33 build_attr: String,34}34}353536async fn build_task(config: Config, hostname: String, build_attr: &str) -> Result<Utf8PathBuf> {36pub(crate) async fn build_task(37 config: Config,38 hostname: String,39 build_attr: &str,cmds/fleet/src/cmds/mod.rsdiffbeforeafterboth4pub mod rollback;4pub mod rollback;5pub mod secrets;5pub mod secrets;6pub mod tf;6pub mod tf;7pub mod usbd;78cmds/fleet/src/cmds/usbd.rsdiffbeforeafterbothno changes
cmds/fleet/src/main.rsdiffbeforeafterboth14 rollback::RollbackSingle,14 rollback::RollbackSingle,15 secrets::Secret,15 secrets::Secret,16 tf::Tf,16 tf::Tf,17 usbd::Usbd,17};18};18use fleet_base::{host::Config, opts::FleetOpts};19use fleet_base::{host::Config, opts::FleetOpts};19use futures::{TryStreamExt, stream::FuturesUnordered};20use futures::{TryStreamExt, stream::FuturesUnordered};74 Prefetch(Prefetch),75 Prefetch(Prefetch),75 /// Config parsing76 /// Config parsing76 Info(Info),77 Info(Info),78 /// Airgapped updates over USB stick79 #[clap(subcommand)]80 Usbd(Usbd),77 /// Command completions81 /// Command completions78 #[clap(hide(true))]82 #[clap(hide(true))]79 Complete(Complete),83 Complete(Complete),100 Opts::RollbackSingle(r) => r.run(config, &opts).await?,104 Opts::RollbackSingle(r) => r.run(config, &opts).await?,101 Opts::Secret(s) => s.run(config, &opts).await?,105 Opts::Secret(s) => s.run(config, &opts).await?,102 Opts::Info(i) => i.run(config).await?,106 Opts::Info(i) => i.run(config).await?,107 Opts::Usbd(u) => u.run(config).await?,103 Opts::Prefetch(p) => p.run(config).await?,108 Opts::Prefetch(p) => p.run(config).await?,104 Opts::Tf(t) => t.run(config).await?,109 Opts::Tf(t) => t.run(config).await?,105 // TODO: actually parse commands before starting the async runtime110 // TODO: actually parse commands before starting the async runtimecmds/usbd/Cargo.tomldiffbeforeafterbothno changes
cmds/usbd/src/main.rsdiffbeforeafterbothno changes
cmds/usbd/src/server.rsdiffbeforeafterbothno changes
crates/fleet-usb/Cargo.tomldiffbeforeafterbothno changes
crates/fleet-usb/src/lib.rsdiffbeforeafterbothno changes
crates/fleet-usb/src/manifest.rsdiffbeforeafterbothno changes
crates/fleet-usb/src/names.rsdiffbeforeafterbothno changes
crates/fleet-usb/src/stream.rsdiffbeforeafterbothno changes
crates/nix-eval/src/lib.ccdiffbeforeafterboth14#include <nix/util/hash.hh>14#include <nix/util/hash.hh>15#include <nix/util/posix-source-accessor.hh>15#include <nix/util/posix-source-accessor.hh>16#include <nix/util/ref.hh>16#include <nix/util/ref.hh>17#include <nix/util/serialise.hh>17#include <nix/util/signature/local-keys.hh>18#include <nix/util/signature/local-keys.hh>18#include <nix/util/signature/signer.hh>19#include <nix/util/signature/signer.hh>19#include <nix/util/source-path.hh>20#include <nix/util/source-path.hh>184 }185 }185}186}187188CxxPathInfo query_path_info(Store *store, rust::Str path) {189 CxxPathInfo out{rust::String(), rust::String(), 0, {}, {}};190 try {191 auto nixStore = store->ptr;192 auto sp = nixStore->parseStorePath(std::string(path));193 auto info = nixStore->queryPathInfo(sp);194 out.nar_hash =195 rust::String(info->narHash.to_string(nix::HashFormat::Nix32, true));196 out.nar_size = info->narSize;197 for (auto &r : info->references)198 out.references.push_back(rust::String(nixStore->printStorePath(r)));199 for (auto &s : info->sigs)200 out.sigs.push_back(rust::String(s.to_string()));201 } catch (const std::exception &e) {202 out.error = rust::String(e.what());203 }204 return out;205}206207CxxBuildResult compute_closure(Store *store, rust::Str path) {208 CxxBuildResult res{rust::String(), {}};209 try {210 auto nixStore = store->ptr;211 auto root = nixStore->parseStorePath(std::string(path));212 nix::StorePathSet closure;213 nixStore->computeFSClosure(root, closure);214 for (auto &p : closure)215 res.outputs.push_back(rust::String(nixStore->printStorePath(p)));216 } catch (const std::exception &e) {217 res.error = rust::String(e.what());218 }219 return res;220}221222namespace {223struct RustFnSink : nix::Sink {224 size_t data;225 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink;226 RustFnSink(size_t data,227 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink)228 : data(data), sink(sink) {}229 void operator()(std::string_view chunk) override {230 bool ok = sink(data, rust::Slice<const uint8_t>(231 reinterpret_cast<const uint8_t *>(chunk.data()),232 chunk.size()));233 if (!ok)234 throw nix::Error("nar sink aborted");235 }236};237} // namespace238239rust::String nar_from_path(Store *store, rust::Str path, size_t sink_data,240 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink) {241 try {242 auto nixStore = store->ptr;243 auto sp = nixStore->parseStorePath(std::string(path));244 RustFnSink s(sink_data, sink);245 nixStore->narFromPath(sp, s);246 return rust::String();247 } catch (const std::exception &e) {248 return rust::String(e.what());249 }250}186251187AddFileToStoreResult add_file_to_store(Store *store, rust::Str name,252AddFileToStoreResult add_file_to_store(Store *store, rust::Str name,188 rust::Str path) {253 rust::Str path) {crates/nix-eval/src/lib.hhdiffbeforeafterboth313132bool is_valid_path(Store *store, rust::Str path);32bool is_valid_path(Store *store, rust::Str path);3334struct CxxPathInfo;35CxxPathInfo query_path_info(Store *store, rust::Str path);3637CxxBuildResult compute_closure(Store *store, rust::Str path);3839rust::String nar_from_path(Store *store, rust::Str path, size_t sink_data,40 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink);3341crates/nix-eval/src/lib.rsdiffbeforeafterboth90 error: String,90 error: String,91 outputs: Vec<String>,91 outputs: Vec<String>,92 }92 }93 struct CxxPathInfo {94 error: String,95 nar_hash: String,96 nar_size: u64,97 references: Vec<String>,98 sigs: Vec<String>,99 }93 unsafe extern "C++" {100 unsafe extern "C++" {94 type nix_fetchers_settings;101 type nix_fetchers_settings;95 type Store;102 type Store;130 #[allow(clippy::missing_safety_doc)]137 #[allow(clippy::missing_safety_doc)]131 unsafe fn is_valid_path(store: *mut Store, path: &str) -> bool;138 unsafe fn is_valid_path(store: *mut Store, path: &str) -> bool;139140 #[allow(clippy::missing_safety_doc)]141 unsafe fn query_path_info(store: *mut Store, path: &str) -> CxxPathInfo;142143 #[allow(clippy::missing_safety_doc)]144 unsafe fn compute_closure(store: *mut Store, path: &str) -> CxxBuildResult;145146 #[allow(clippy::missing_safety_doc)]147 unsafe fn nar_from_path(148 store: *mut Store,149 path: &str,150 sink_data: usize,151 sink: fn(usize, &[u8]) -> bool,152 ) -> String;132 }153 }133}154}134155535unsafe impl Send for Store {}556unsafe impl Send for Store {}536unsafe impl Sync for Store {}557unsafe impl Sync for Store {}558559#[derive(Debug, Clone)]560pub struct PathInfo {561 pub nar_hash: String,562 pub nar_size: u64,563 pub references: Vec<Utf8PathBuf>,564 pub sigs: Vec<String>,565}537566538pub fn eval_store() -> Arc<Store> {567pub fn eval_store() -> Arc<Store> {539 GLOBAL_STATE.store.clone()568 GLOBAL_STATE.store.clone()622 unsafe { nix_cxx::is_valid_path(self.as_ptr().cast(), path.as_str()) }651 unsafe { nix_cxx::is_valid_path(self.as_ptr().cast(), path.as_str()) }623 }652 }653654 #[instrument(skip(self))]655 pub fn query_path_info(&self, path: &Utf8Path) -> Result<PathInfo> {656 let res = unsafe { nix_cxx::query_path_info(self.as_ptr().cast(), path.as_str()) };657 if !res.error.is_empty() {658 bail!("failed to query path info for {path}: {}", res.error);659 }660 Ok(PathInfo {661 nar_hash: res.nar_hash,662 nar_size: res.nar_size,663 references: res.references.into_iter().map(Utf8PathBuf::from).collect(),664 sigs: res.sigs,665 })666 }667668 #[instrument(skip(self))]669 pub fn compute_closure(&self, path: &Utf8Path) -> Result<Vec<Utf8PathBuf>> {670 let res = unsafe { nix_cxx::compute_closure(self.as_ptr().cast(), path.as_str()) };671 if !res.error.is_empty() {672 bail!("failed to compute closure of {path}: {}", res.error);673 }674 Ok(res.outputs.into_iter().map(Utf8PathBuf::from).collect())675 }676677 #[instrument(skip(self, out))]678 pub fn nar_from_path(&self, path: &Utf8Path, out: &mut dyn std::io::Write) -> Result<()> {679 struct SinkState<'a> {680 out: &'a mut dyn std::io::Write,681 error: Option<std::io::Error>,682 }683 fn sink(state: usize, data: &[u8]) -> bool {684 let state = unsafe { &mut *(state as *mut SinkState) };685 match state.out.write_all(data) {686 Ok(()) => true,687 Err(e) => {688 state.error = Some(e);689 false690 }691 }692 }693 let mut state = SinkState { out, error: None };694 let msg = unsafe {695 nix_cxx::nar_from_path(696 self.as_ptr().cast(),697 path.as_str(),698 (&raw mut state) as usize,699 sink,700 )701 };702 if let Some(e) = state.error {703 return Err(anyhow!(e).context(format!("nar sink failed for {path}")));704 }705 if !msg.is_empty() {706 bail!("failed to dump nar of {path}: {msg}");707 }708 Ok(())709 }624710625 #[instrument(skip(self))]711 #[instrument(skip(self))]626 pub fn build_drv_outputs(712 pub fn build_drv_outputs(docs/features/usbd.adocdiffbeforeafterbothno changes
lib/flakePart.nixdiffbeforeafterboth72 })72 })73 fleet-install-secrets73 fleet-install-secrets74 fleet-generator-helper74 fleet-generator-helper75 fleet-usbd75 remowt-plugin-fleet76 remowt-plugin-fleet76 ;77 ;77 })78 })modules/nixos/module-list.nixdiffbeforeafterboth3 ./secrets.nix3 ./secrets.nix4 ./rollback.nix4 ./rollback.nix5 ./nix-sign.nix5 ./nix-sign.nix6 ./usbd.nix6 ./online.nix7 ./online.nix7 ./polkit.nix8 ./polkit.nix8 ./top-level.nix9 ./top-level.nixmodules/nixos/usbd.nixdiffbeforeafterbothno changes
pkgs/default.nixdiffbeforeafterboth10 fleet = callPackage ./fleet.nix { inherit craneLib inputs remowt-agents-bundle; };10 fleet = callPackage ./fleet.nix { inherit craneLib inputs remowt-agents-bundle; };11 fleet-install-secrets = callPackage ./fleet-install-secrets.nix { inherit craneLib; };11 fleet-install-secrets = callPackage ./fleet-install-secrets.nix { inherit craneLib; };12 fleet-generator-helper = callPackage ./fleet-generator-helper.nix { inherit craneLib; };12 fleet-generator-helper = callPackage ./fleet-generator-helper.nix { inherit craneLib; };13 fleet-usbd = callPackage ./fleet-usbd.nix { inherit craneLib inputs; };131414 inherit remowt-agents-bundle;15 inherit remowt-agents-bundle;15 remowt-plugin-fleet = callPackage ./remowt-plugin-fleet.nix { inherit craneLib inputs; };16 remowt-plugin-fleet = callPackage ./remowt-plugin-fleet.nix { inherit craneLib inputs; };pkgs/fleet-usbd.nixdiffbeforeafterbothno changes