-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add cloud-init volume generation to standalone
Because some of the maths (copied from Omicron) to generated the FAT volume make use of usize::div_ceil(), the toolchain of propolis-standalone was bumped to 1.73. This necessitated some unrelated clippy fixes, as well as updates to the GHA build configuration to support said latest version.
- Loading branch information
Showing
10 changed files
with
187 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use std::io::{Cursor, Write}; | ||
use std::sync::Arc; | ||
|
||
use anyhow::{bail, Context}; | ||
use fatfs::{FileSystem, FormatVolumeOptions, FsOptions}; | ||
|
||
use propolis::block; | ||
use propolis_standalone_config::Config; | ||
|
||
const SECTOR_SZ: usize = 512; | ||
const VOLUME_LABEL: [u8; 11] = *b"cidata "; | ||
|
||
pub(crate) fn build_cidata_be( | ||
config: &Config, | ||
) -> anyhow::Result<Arc<block::InMemoryBackend>> { | ||
let cidata = &config.cloudinit.as_ref().ok_or_else(|| { | ||
anyhow::anyhow!("missing [cloudinit] config section") | ||
})?; | ||
|
||
let fields = [ | ||
("user-data", &cidata.user_data, &cidata.user_data_path), | ||
("meta-data", &cidata.meta_data, &cidata.meta_data_path), | ||
("network-config", &cidata.network_config, &cidata.network_config_path), | ||
]; | ||
let all_data = fields | ||
.into_iter() | ||
.map(|(name, str_data, path_data)| { | ||
Ok(( | ||
name, | ||
match (str_data, path_data) { | ||
(None, None) => vec![], | ||
(None, Some(path)) => std::fs::read(path).context( | ||
format!("unable to read {name} from {path}"), | ||
)?, | ||
(Some(data), None) => data.clone().into(), | ||
(Some(_), Some(_)) => { | ||
bail!("cannot provide path and string for {name}"); | ||
} | ||
}, | ||
)) | ||
}) | ||
.collect::<Result<Vec<_>, _>>()?; | ||
|
||
let file_sectors: usize = | ||
all_data.iter().map(|(_, data)| data.len().div_ceil(SECTOR_SZ)).sum(); | ||
// vfat can hold more data than this, but we don't expect to ever need that | ||
// for cloud-init purposes. | ||
if file_sectors > 512 { | ||
bail!("too much vfat data: {file_sectors} > 512 sectors"); | ||
} | ||
|
||
// Copying the match already done for this in Omicron: | ||
// | ||
// if we're storing < 341 KiB of clusters, the overhead is 37. With a limit | ||
// of 512 sectors (error check above), we can assume an overhead of 37. | ||
// Additionally, fatfs refuses to format a disk that is smaller than 42 | ||
// sectors. | ||
let sectors = 42.max(file_sectors + 37); | ||
|
||
// Some tools also require that the number of sectors is a multiple of the | ||
// sectors-per-track. fatfs uses a default of 32 which won't evenly divide | ||
// sectors as we compute above generally. To fix that we simply set it to | ||
// match the number of sectors to make it trivially true. | ||
let sectors_per_track = sectors.try_into().unwrap(); | ||
|
||
let mut disk = Cursor::new(vec![0; sectors * SECTOR_SZ]); | ||
fatfs::format_volume( | ||
&mut disk, | ||
FormatVolumeOptions::new() | ||
.bytes_per_cluster(512) | ||
.sectors_per_track(sectors_per_track) | ||
.fat_type(fatfs::FatType::Fat12) | ||
.volume_label(VOLUME_LABEL), | ||
) | ||
.context("error formatting FAT volume")?; | ||
|
||
let fs = FileSystem::new(&mut disk, FsOptions::new())?; | ||
let root_dir = fs.root_dir(); | ||
for (name, data) in all_data.iter() { | ||
if *name == "network-config" && data.is_empty() { | ||
// Skip creating an empty network interfaces if nothing is provided. | ||
// It is not required, unlike the other files | ||
} | ||
root_dir.create_file(name)?.write_all(data)?; | ||
} | ||
drop(root_dir); | ||
drop(fs); | ||
|
||
block::InMemoryBackend::create( | ||
disk.into_inner(), | ||
block::BackendOpts { | ||
block_size: Some(SECTOR_SZ as u32), | ||
read_only: Some(true), | ||
..Default::default() | ||
}, | ||
) | ||
.context("could not create block backend") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters