Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC/RFC: Support reinstalls via kexec #791

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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