Skip to content

Commit

Permalink
POC: Support reinstalls via kexec
Browse files Browse the repository at this point in the history
  • Loading branch information
jlebon committed Jan 10, 2022
1 parent e7154db commit 8c78dbc
Show file tree
Hide file tree
Showing 4 changed files with 522 additions and 3 deletions.
97 changes: 97 additions & 0 deletions src/cmdline.rs
Expand Up @@ -46,6 +46,8 @@ use crate::io::IgnitionHash;
pub enum Cmd {
/// Install Fedora CoreOS or RHEL CoreOS
Install(InstallConfig),
/// Re-Install Fedora CoreOS or RHEL CoreOS
Reinstall(ReinstallConfig),
/// Download a CoreOS image
Download(DownloadConfig),
/// List available images in a Fedora CoreOS stream
Expand Down Expand Up @@ -454,6 +456,101 @@ pub enum PartitionFilter {
Index(Option<NonZeroU32>, Option<NonZeroU32>),
}

#[derive(Debug, StructOpt)]
pub struct ReinstallConfig {
// These are all the options we can drive purely from kargs.
// XXX: should dedupe with `InstallConfig` and reuse the config-file mechanism.

// ways to specify the image source
/// Fedora CoreOS stream
#[structopt(short, long, value_name = "name")]
#[structopt(conflicts_with = "image-url")]
pub stream: Option<String>,
/// Manually specify the image URL
#[structopt(short = "u", long, value_name = "URL")]
#[structopt(conflicts_with = "stream")]
pub image_url: Option<Url>,

// postprocessing options
/// Embed an Ignition config from a URL
#[structopt(short = "I", long, value_name = "URL")]
#[structopt(conflicts_with = "ignition-file")]
pub ignition_url: Option<Url>,
/// Override the Ignition platform ID
#[structopt(short, long, value_name = "name")]
pub platform: Option<String>,
/// Save partitions with this label glob
#[structopt(long, value_name = "lx")]
// Allow argument multiple times, but one value each. Allow "a,b" in
// one argument.
#[structopt(number_of_values = 1, require_delimiter = true)]
pub save_partlabel: Vec<String>,
/// Save partitions with this number or range
#[structopt(long, value_name = "id")]
// Allow argument multiple times, but one value each. Allow "1-5,7" in
// one argument.
#[structopt(number_of_values = 1, require_delimiter = true)]
// Allow ranges like "-2".
#[structopt(allow_hyphen_values = true)]
pub save_partindex: Vec<String>,

// obscure options without short names
/// Skip signature verification
// XXX: should have independent switch for immediate vs delayed (e.g. image-url) fetches
#[structopt(long)]
pub insecure: bool,
/// Fetch retries, or "infinite"
#[structopt(long, value_name = "N", default_value)]
pub fetch_retries: FetchRetries,

// positional args
/// Destination device
pub dest_device: String,

// specific to `reinstall`
/// Skip rebooting after reinstall
#[structopt(long)]
pub skip_reboot: bool,
/// URL to initramfs
#[structopt(
long,
value_name = "URL",
conflicts_with = "initramfs-file",
requires = "rootfs-url"
)]
pub initramfs_url: Option<Url>,
/// Local path to initramfs
#[structopt(
long,
value_name = "PATH",
conflicts_with = "initramfs-url",
requires = "rootfs-file"
)]
pub initramfs_file: Option<String>,
/// URL to rootfs
#[structopt(
long,
value_name = "URL",
conflicts_with = "rootfs-file",
requires = "initramfs-url"
)]
pub rootfs_url: Option<Url>,
/// Local path to rootfs
#[structopt(
long,
value_name = "PATH",
conflicts_with = "rootfs-url",
requires = "initramfs-file"
)]
pub rootfs_file: Option<String>,
/// Use `coreos.live.rootfs_url` instead of appending.
#[structopt(long, conflicts_with = "rootfs-file")]
pub defer_rootfs: bool,
/// Serial console to use
#[structopt(long, value_name = "KARG", default_value = "ttyS0")]
pub console: String,
}

#[derive(Debug, StructOpt)]
pub struct DownloadConfig {
/// Fedora CoreOS stream
Expand Down
44 changes: 44 additions & 0 deletions src/download.rs
Expand Up @@ -410,6 +410,50 @@ pub fn download_to_tempfile(url: &Url, retries: FetchRetries) -> Result<File> {
Ok(f)
}

// XXX: try to dedupe with write_image?
pub fn download_and_verify_to_tempfile(
url: &Url,
sig_url: &Url,
retries: FetchRetries,
) -> Result<File> {
let sig: Vec<u8> = {
let client = new_http_client()?;
let mut resp = http_get(client, sig_url, retries).context("fetching signature URL")?;
let mut sig_bytes = Vec::new();
resp.read_to_end(&mut sig_bytes)
.context("reading signature content")?;
sig_bytes
};

let client = new_http_client()?;
let mut resp = http_get(client, url, retries)?;

// wrap source for signature verification, if available
// keep the reader so we can explicitly check the result afterward
let mut verify_reader =
VerifyReader::new(&mut resp, Some(sig.as_slice()), VerifyKeys::Production)?;

// wrap in a BufReader to amortize read overhead
let mut reader = BufReader::with_capacity(BUFFER_SIZE, &mut verify_reader);

let mut f = tempfile::tempfile()?;
let mut writer = BufWriter::with_capacity(BUFFER_SIZE, &mut f);
copy(&mut reader, &mut writer).with_context(|| format!("couldn't copy '{}'", url))?;

// check signature
drop(reader);
verify_reader.verify()?;

writer
.flush()
.with_context(|| format!("couldn't write '{}' to disk", url))?;
drop(writer);
f.seek(SeekFrom::Start(0))
.with_context(|| format!("rewinding file for '{}'", url))?;

Ok(f)
}

struct ProgressReader<'a, R: Read> {
source: R,
length: Option<(NonZeroU64, String)>,
Expand Down

0 comments on commit 8c78dbc

Please sign in to comment.