0.3.2 final cleanup (#1782)

* bump version with stubbed release notes

* increase BE timeout

* 032 release notes

* hide developer menu for now

* remove unused sub/import

* remoce reconnect from disks res in setup wiz

* remove quirks

* flatten drives response

Co-authored-by: Matt Hill <matthewonthemoon@gmail.com>
This commit is contained in:
Aiden McClelland
2022-09-08 16:14:42 -06:00
committed by GitHub
parent 5442459b2d
commit b9ce2bf2dc
32 changed files with 154 additions and 395 deletions

View File

@@ -1,6 +1,5 @@
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use std::sync::Arc;
use chrono::Utc;
use clap::ArgMatches;
@@ -8,7 +7,7 @@ use color_eyre::eyre::eyre;
use helpers::AtomicFile;
use openssl::pkey::{PKey, Private};
use openssl::x509::X509;
use patch_db::{DbHandle, LockType, PatchDbHandle, Revision};
use patch_db::{DbHandle, LockType, PatchDbHandle};
use rpc_toolkit::command;
use serde::{Deserialize, Serialize};
use serde_json::Value;

View File

@@ -9,7 +9,7 @@ use color_eyre::eyre::eyre;
use futures::future::BoxFuture;
use futures::FutureExt;
use openssl::x509::X509;
use patch_db::{DbHandle, PatchDbHandle, Revision};
use patch_db::{DbHandle, PatchDbHandle};
use rpc_toolkit::command;
use tokio::fs::File;
use tokio::task::JoinHandle;
@@ -57,8 +57,7 @@ pub async fn restore_packages_rpc(
let backup_guard =
BackupMountGuard::mount(TmpMountGuard::mount(&fs, ReadOnly).await?, &password).await?;
let (revision, backup_guard, tasks, _) =
restore_packages(&ctx, &mut db, backup_guard, ids).await?;
let (backup_guard, tasks, _) = restore_packages(&ctx, &mut db, backup_guard, ids).await?;
tokio::spawn(async move {
let res = futures::future::join_all(tasks).await;
@@ -246,7 +245,7 @@ pub async fn recover_full_embassy(
.keys()
.cloned()
.collect();
let (_, backup_guard, tasks, progress_info) = restore_packages(
let (backup_guard, tasks, progress_info) = restore_packages(
&rpc_ctx,
&mut db,
backup_guard,
@@ -304,14 +303,13 @@ async fn restore_packages(
ids: Vec<PackageId>,
) -> Result<
(
Option<Arc<Revision>>,
BackupMountGuard<TmpMountGuard>,
Vec<JoinHandle<(Result<(), Error>, PackageId)>>,
ProgressInfo,
),
Error,
> {
let (revision, guards) = assure_restoring(ctx, db, ids, &backup_guard).await?;
let guards = assure_restoring(ctx, db, ids, &backup_guard).await?;
let mut progress_info = ProgressInfo::default();
@@ -339,7 +337,7 @@ async fn restore_packages(
));
}
Ok((revision, backup_guard, tasks, progress_info))
Ok((backup_guard, tasks, progress_info))
}
#[instrument(skip(ctx, db, backup_guard))]
@@ -348,13 +346,7 @@ async fn assure_restoring(
db: &mut PatchDbHandle,
ids: Vec<PackageId>,
backup_guard: &BackupMountGuard<TmpMountGuard>,
) -> Result<
(
Option<Arc<Revision>>,
Vec<(Manifest, PackageBackupMountGuard)>,
),
Error,
> {
) -> Result<Vec<(Manifest, PackageBackupMountGuard)>, Error> {
let mut tx = db.begin().await?;
let mut guards = Vec::with_capacity(ids.len());
@@ -414,7 +406,8 @@ async fn assure_restoring(
guards.push((manifest, guard));
}
Ok((tx.commit().await?, guards))
tx.commit().await?;
Ok(guards)
}
#[instrument(skip(ctx, guard))]

View File

@@ -134,7 +134,6 @@ pub fn target() -> Result<(), Error> {
Ok(())
}
// TODO: incorporate reconnect into this response as well
#[command(display(display_serializable))]
pub async fn list(
#[context] ctx: RpcContext,
@@ -143,7 +142,6 @@ pub async fn list(
let (disks_res, cifs) =
tokio::try_join!(crate::disk::util::list(), cifs::list(&mut sql_handle),)?;
Ok(disks_res
.disks
.into_iter()
.flat_map(|mut disk| {
std::mem::take(&mut disk.partitions)

View File

@@ -77,7 +77,7 @@ async fn subscribe_to_session_kill(
async fn deal_with_messages(
_has_valid_authentication: HasValidSession,
mut kill: oneshot::Receiver<()>,
mut sub: patch_db::Subscriber,
sub: patch_db::Subscriber,
mut stream: WebSocketStream<Upgraded>,
) -> Result<(), Error> {
loop {

View File

@@ -1,7 +1,7 @@
use clap::ArgMatches;
use rpc_toolkit::command;
use self::util::DiskListResponse;
use crate::disk::util::DiskInfo;
use crate::util::display_none;
use crate::util::serde::{display_serializable, IoFormat};
use crate::Error;
@@ -9,7 +9,6 @@ use crate::Error;
pub mod fsck;
pub mod main;
pub mod mount;
pub mod quirks;
pub mod util;
pub const BOOT_RW_PATH: &str = "/media/boot-rw";
@@ -20,7 +19,7 @@ pub fn disk() -> Result<(), Error> {
Ok(())
}
fn display_disk_info(info: DiskListResponse, matches: &ArgMatches) {
fn display_disk_info(info: Vec<DiskInfo>, matches: &ArgMatches) {
use prettytable::*;
if matches.is_present("format") {
@@ -35,7 +34,7 @@ fn display_disk_info(info: DiskListResponse, matches: &ArgMatches) {
"USED",
"EMBASSY OS VERSION"
]);
for disk in info.disks {
for disk in info {
let row = row![
disk.logicalname.display(),
"N/A",
@@ -79,7 +78,7 @@ pub async fn list(
#[allow(unused_variables)]
#[arg]
format: Option<IoFormat>,
) -> Result<DiskListResponse, Error> {
) -> Result<Vec<DiskInfo>, Error> {
crate::disk::util::list().await
}

View File

@@ -1,172 +0,0 @@
use std::collections::BTreeSet;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
use color_eyre::eyre::eyre;
use helpers::AtomicFile;
use tokio::io::AsyncWriteExt;
use tracing::instrument;
use super::BOOT_RW_PATH;
use crate::{Error, ErrorKind, ResultExt};
pub const QUIRK_PATH: &'static str = "/sys/module/usb_storage/parameters/quirks";
pub const WHITELIST: [(VendorId, ProductId); 5] = [
(VendorId(0x1d6b), ProductId(0x0002)), // root hub usb2
(VendorId(0x1d6b), ProductId(0x0003)), // root hub usb3
(VendorId(0x2109), ProductId(0x3431)),
(VendorId(0x1058), ProductId(0x262f)), // western digital black HDD
(VendorId(0x04e8), ProductId(0x4001)), // Samsung T7
];
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct VendorId(u16);
impl std::str::FromStr for VendorId {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
u16::from_str_radix(s.trim(), 16).map(VendorId)
}
}
impl std::fmt::Display for VendorId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:04x}", self.0)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct ProductId(u16);
impl std::str::FromStr for ProductId {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
u16::from_str_radix(s.trim(), 16).map(ProductId)
}
}
impl std::fmt::Display for ProductId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:04x}", self.0)
}
}
#[derive(Clone, Debug)]
pub struct Quirks(BTreeSet<(VendorId, ProductId)>);
impl Quirks {
pub fn add(&mut self, vendor: VendorId, product: ProductId) {
self.0.insert((vendor, product));
}
pub fn remove(&mut self, vendor: VendorId, product: ProductId) {
self.0.remove(&(vendor, product));
}
pub fn contains(&self, vendor: VendorId, product: ProductId) -> bool {
self.0.contains(&(vendor, product))
}
}
impl std::fmt::Display for Quirks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut comma = false;
for (vendor, product) in &self.0 {
if comma {
write!(f, ",")?;
} else {
comma = true;
}
write!(f, "{}:{}:u", vendor, product)?;
}
Ok(())
}
}
impl std::str::FromStr for Quirks {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let mut quirks = BTreeSet::new();
for item in s.split(",") {
if let [vendor, product, "u"] = item.splitn(3, ":").collect::<Vec<_>>().as_slice() {
quirks.insert((vendor.parse()?, product.parse()?));
} else {
return Err(Error::new(
eyre!("Invalid quirk: `{}`", item),
crate::ErrorKind::DiskManagement,
));
}
}
Ok(Quirks(quirks))
}
}
#[instrument]
pub async fn update_quirks(quirks: &mut Quirks) -> Result<Vec<String>, Error> {
let mut usb_devices = tokio::fs::read_dir("/sys/bus/usb/devices/").await?;
let mut to_reconnect = Vec::new();
while let Some(usb_device) = usb_devices.next_entry().await? {
if tokio::fs::metadata(usb_device.path().join("idVendor"))
.await
.is_err()
{
continue;
}
let vendor = tokio::fs::read_to_string(usb_device.path().join("idVendor"))
.await?
.parse()?;
let product = tokio::fs::read_to_string(usb_device.path().join("idProduct"))
.await?
.parse()?;
if WHITELIST.contains(&(vendor, product)) {
quirks.remove(vendor, product);
continue;
}
if quirks.contains(vendor, product) {
continue;
}
quirks.add(vendor, product);
{
// write quirks to sysfs
let mut quirk_file = tokio::fs::File::create(QUIRK_PATH).await?;
quirk_file.write_all(quirks.to_string().as_bytes()).await?;
quirk_file.sync_all().await?;
drop(quirk_file);
}
disconnect_usb(usb_device.path()).await?;
let (vendor_name, product_name) = tokio::try_join!(
tokio::fs::read_to_string(usb_device.path().join("manufacturer")),
tokio::fs::read_to_string(usb_device.path().join("product")),
)?;
to_reconnect.push(format!("{} {}", vendor_name, product_name));
}
Ok(to_reconnect)
}
#[instrument(skip(usb_device_path))]
pub async fn disconnect_usb(usb_device_path: impl AsRef<Path>) -> Result<(), Error> {
let authorized_path = usb_device_path.as_ref().join("bConfigurationValue");
let mut authorized_file = tokio::fs::File::create(&authorized_path).await?;
authorized_file.write_all(b"0").await?;
authorized_file.sync_all().await?;
drop(authorized_file);
Ok(())
}
#[instrument]
pub async fn fetch_quirks() -> Result<Quirks, Error> {
Ok(tokio::fs::read_to_string(QUIRK_PATH).await?.parse()?)
}
#[instrument]
pub async fn save_quirks(quirks: &Quirks) -> Result<(), Error> {
let orig_path = Path::new(BOOT_RW_PATH).join("cmdline.txt.orig");
let target_path = Path::new(BOOT_RW_PATH).join("cmdline.txt");
if tokio::fs::metadata(&orig_path).await.is_err() {
tokio::fs::copy(&target_path, &orig_path).await?;
}
let cmdline = tokio::fs::read_to_string(&orig_path).await?;
let mut target = AtomicFile::new(&target_path, None::<PathBuf>)
.await
.with_kind(ErrorKind::Filesystem)?;
target
.write_all(format!("usb-storage.quirks={} {}", quirks, cmdline).as_bytes())
.await?;
target.save().await.with_kind(ErrorKind::Filesystem)?;
Ok(())
}

View File

@@ -19,19 +19,11 @@ use tracing::instrument;
use super::mount::filesystem::block_dev::BlockDev;
use super::mount::filesystem::ReadOnly;
use super::mount::guard::TmpMountGuard;
use super::quirks::{fetch_quirks, save_quirks, update_quirks};
use crate::util::io::from_yaml_async_reader;
use crate::util::serde::IoFormat;
use crate::util::{Invoke, Version};
use crate::{Error, ResultExt as _};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DiskListResponse {
pub disks: Vec<DiskInfo>,
pub reconnect: Vec<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DiskInfo {
@@ -240,10 +232,7 @@ pub async fn recovery_info(
}
#[instrument]
pub async fn list() -> Result<DiskListResponse, Error> {
let mut quirks = fetch_quirks().await?;
let reconnect = update_quirks(&mut quirks).await?;
save_quirks(&mut quirks).await?;
pub async fn list() -> Result<Vec<DiskInfo>, Error> {
let disk_guids = pvscan().await?;
let disks = tokio_stream::wrappers::ReadDirStream::new(
tokio::fs::read_dir(DISK_PATH)
@@ -374,10 +363,7 @@ pub async fn list() -> Result<DiskListResponse, Error> {
})
}
Ok(DiskListResponse {
disks: res,
reconnect,
})
Ok(res)
}
fn parse_pvscan_output(pvscan_output: &str) -> BTreeMap<PathBuf, Option<String>> {

View File

@@ -508,26 +508,26 @@ pub async fn follow_logs(
// println!("{}", serialized);
// }
#[tokio::test]
pub async fn test_logs() {
let mut cmd = Command::new("journalctl");
cmd.kill_on_drop(true);
// #[tokio::test]
// pub async fn test_logs() {
// let mut cmd = Command::new("journalctl");
// cmd.kill_on_drop(true);
cmd.arg("-f");
cmd.arg("CONTAINER_NAME=hello-world.embassy");
// cmd.arg("-f");
// cmd.arg("CONTAINER_NAME=hello-world.embassy");
let mut child = cmd.stdout(Stdio::piped()).spawn().unwrap();
let out = BufReader::new(
child
.stdout
.take()
.ok_or_else(|| Error::new(eyre!("No stdout available"), crate::ErrorKind::Journald))
.unwrap(),
);
// let mut child = cmd.stdout(Stdio::piped()).spawn().unwrap();
// let out = BufReader::new(
// child
// .stdout
// .take()
// .ok_or_else(|| Error::new(eyre!("No stdout available"), crate::ErrorKind::Journald))
// .unwrap(),
// );
let mut journalctl_entries = LinesStream::new(out.lines());
// let mut journalctl_entries = LinesStream::new(out.lines());
while let Some(line) = journalctl_entries.try_next().await.unwrap() {
dbg!(line);
}
}
// while let Some(line) = journalctl_entries.try_next().await.unwrap() {
// dbg!(line);
// }
// }

View File

@@ -94,7 +94,6 @@ impl MdnsControllerInner {
}
self.sync();
}
fn free(&self) {}
}
fn log_str_error(action: &str, e: i32) {

View File

@@ -36,7 +36,7 @@ use crate::disk::mount::filesystem::block_dev::BlockDev;
use crate::disk::mount::filesystem::cifs::Cifs;
use crate::disk::mount::filesystem::ReadOnly;
use crate::disk::mount::guard::TmpMountGuard;
use crate::disk::util::{pvscan, recovery_info, DiskListResponse, EmbassyOsRecoveryInfo};
use crate::disk::util::{pvscan, recovery_info, DiskInfo, EmbassyOsRecoveryInfo};
use crate::disk::REPAIR_DISK_PATH;
use crate::hostname::{get_hostname, Hostname};
use crate::id::Id;
@@ -87,7 +87,7 @@ pub fn disk() -> Result<(), Error> {
}
#[command(rename = "list", rpc_only, metadata(authenticated = false))]
pub async fn list_disks() -> Result<DiskListResponse, Error> {
pub async fn list_disks() -> Result<Vec<DiskInfo>, Error> {
crate::disk::list(None).await
}

View File

@@ -15,8 +15,9 @@ mod v0_3_0_3;
mod v0_3_1;
mod v0_3_1_1;
mod v0_3_1_2;
mod v0_3_2;
pub type Current = v0_3_1_2::Version;
pub type Current = v0_3_2::Version;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(untagged)]
@@ -28,6 +29,7 @@ enum Version {
V0_3_1(Wrapper<v0_3_1::Version>),
V0_3_1_1(Wrapper<v0_3_1_1::Version>),
V0_3_1_2(Wrapper<v0_3_1_2::Version>),
V0_3_2(Wrapper<v0_3_2::Version>),
Other(emver::Version),
}
@@ -50,6 +52,7 @@ impl Version {
Version::V0_3_1(Wrapper(x)) => x.semver(),
Version::V0_3_1_1(Wrapper(x)) => x.semver(),
Version::V0_3_1_2(Wrapper(x)) => x.semver(),
Version::V0_3_2(Wrapper(x)) => x.semver(),
Version::Other(x) => x.clone(),
}
}
@@ -183,6 +186,7 @@ pub async fn init<Db: DbHandle>(
Version::V0_3_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_1_1(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_1_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::V0_3_2(v) => v.0.migrate_to(&Current::new(), db, receipts).await?,
Version::Other(_) => {
return Err(Error::new(
eyre!("Cannot downgrade"),
@@ -221,6 +225,8 @@ mod tests {
Just(Version::V0_3_0_3(Wrapper(v0_3_0_3::Version::new()))),
Just(Version::V0_3_1(Wrapper(v0_3_1::Version::new()))),
Just(Version::V0_3_1_1(Wrapper(v0_3_1_1::Version::new()))),
Just(Version::V0_3_1_2(Wrapper(v0_3_1_2::Version::new()))),
Just(Version::V0_3_2(Wrapper(v0_3_2::Version::new()))),
em_version().prop_map(Version::Other),
]
}

View File

@@ -4,7 +4,6 @@ use emver::VersionRange;
use tokio::process::Command;
use super::*;
use crate::disk::quirks::{fetch_quirks, save_quirks, update_quirks};
use crate::disk::BOOT_RW_PATH;
use crate::update::query_mounted_label;
use crate::util::Invoke;
@@ -36,9 +35,6 @@ impl VersionT for Version {
.arg(Path::new(BOOT_RW_PATH).join("cmdline.txt.orig"))
.invoke(crate::ErrorKind::Filesystem)
.await?;
let mut q = fetch_quirks().await?;
update_quirks(&mut q).await?;
save_quirks(&q).await?;
Ok(())
}
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {

View File

@@ -1,7 +1,5 @@
use emver::VersionRange;
use crate::hostname::{generate_id, get_hostname, sync_hostname};
use super::v0_3_0::V0_3_0_COMPAT;
use super::*;
@@ -21,41 +19,10 @@ impl VersionT for Version {
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
let hostname = get_hostname(db).await?;
crate::db::DatabaseModel::new()
.server_info()
.hostname()
.put(db, &Some(hostname.0))
.await?;
crate::db::DatabaseModel::new()
.server_info()
.id()
.put(db, &generate_id())
.await?;
sync_hostname(db).await?;
let mut ui = crate::db::DatabaseModel::new()
.ui()
.get(db, false)
.await?
.clone();
if let serde_json::Value::Object(ref mut ui) = ui {
ui.insert("ack-instructions".to_string(), serde_json::json!({}));
}
crate::db::DatabaseModel::new().ui().put(db, &ui).await?;
async fn up<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
Ok(())
}
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
let mut ui = crate::db::DatabaseModel::new()
.ui()
.get(db, false)
.await?
.clone();
if let serde_json::Value::Object(ref mut ui) = ui {
ui.remove("ack-instructions");
}
crate::db::DatabaseModel::new().ui().put(db, &ui).await?;
async fn down<Db: DbHandle>(&self, _db: &mut Db) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -0,0 +1,61 @@
use emver::VersionRange;
use crate::hostname::{generate_id, get_hostname, sync_hostname};
use super::v0_3_0::V0_3_0_COMPAT;
use super::*;
const V0_3_2: emver::Version = emver::Version::new(0, 3, 2, 0);
#[derive(Clone, Debug)]
pub struct Version;
#[async_trait]
impl VersionT for Version {
type Previous = v0_3_1_2::Version;
fn new() -> Self {
Version
}
fn semver(&self) -> emver::Version {
V0_3_2
}
fn compat(&self) -> &'static VersionRange {
&*V0_3_0_COMPAT
}
async fn up<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
let hostname = get_hostname(db).await?;
crate::db::DatabaseModel::new()
.server_info()
.hostname()
.put(db, &Some(hostname.0))
.await?;
crate::db::DatabaseModel::new()
.server_info()
.id()
.put(db, &generate_id())
.await?;
sync_hostname(db).await?;
let mut ui = crate::db::DatabaseModel::new()
.ui()
.get(db, false)
.await?
.clone();
if let serde_json::Value::Object(ref mut ui) = ui {
ui.insert("ack-instructions".to_string(), serde_json::json!({}));
}
crate::db::DatabaseModel::new().ui().put(db, &ui).await?;
Ok(())
}
async fn down<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
let mut ui = crate::db::DatabaseModel::new()
.ui()
.get(db, false)
.await?
.clone();
if let serde_json::Value::Object(ref mut ui) = ui {
ui.remove("ack-instructions");
}
crate::db::DatabaseModel::new().ui().put(db, &ui).await?;
Ok(())
}
}