forked from cncf-tags/container-device-interface-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
oci-runtime-generate: Implement JSON generation for OCI runtime
In cdi-go, the oci-runtime-tools generate tool is used to generate configuration JSON for the OCI runtime. However, since it is not possible to use existing libraries like cdi-go, we had to implement this functionality ourselves. Additionally, since the pr for oci-spec-rs (youki-dev/oci-spec-rs#166) has been merged but no release version exists yet, we'll utilize the below method for dependency management: oci-spec = { git = "https://github.com/containers/oci-spec-rs.git", rev = "d338cf8", features = ["runtime"] } Signed-off-by: Alex Lyn <[email protected]>
- Loading branch information
Showing
5 changed files
with
347 additions
and
1 deletion.
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
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,85 @@ | ||
use std::collections::HashMap; | ||
|
||
use oci_spec::runtime::{Hooks, Linux, LinuxIntelRdt, LinuxResources, Mount, Process, Spec}; | ||
|
||
pub struct Generator { | ||
pub config: Option<Spec>, | ||
pub host_specific: bool, | ||
pub env_map: HashMap<String, usize>, | ||
} | ||
|
||
impl Generator { | ||
pub fn spec_gen(spec: Option<Spec>) -> Self { | ||
Generator { | ||
config: spec, | ||
host_specific: false, | ||
env_map: HashMap::new(), | ||
} | ||
} | ||
|
||
pub fn init_config(&mut self) { | ||
if self.config.is_none() { | ||
self.config = Some(Spec::default()); | ||
} | ||
} | ||
|
||
pub fn init_config_process(&mut self) { | ||
self.init_config(); | ||
|
||
if let Some(spec) = self.config.as_mut() { | ||
if spec.process().is_none() { | ||
spec.set_process(Some(Process::default())); | ||
} | ||
} | ||
} | ||
|
||
pub fn init_config_linux(&mut self) { | ||
self.init_config(); | ||
|
||
if let Some(spec) = self.config.as_mut() { | ||
if spec.linux().is_none() { | ||
spec.set_linux(Some(Linux::default())); | ||
} | ||
} | ||
} | ||
|
||
pub fn init_config_linux_resources(&mut self) { | ||
self.init_config_linux(); | ||
|
||
if let Some(linux) = self.config.as_mut().unwrap().linux_mut() { | ||
if linux.resources().is_none() { | ||
linux.set_resources(Some(LinuxResources::default())); | ||
} | ||
} | ||
} | ||
|
||
pub fn init_config_hooks(&mut self) { | ||
self.init_config(); | ||
|
||
if let Some(spec) = self.config.as_mut() { | ||
if spec.hooks().is_none() { | ||
spec.set_hooks(Some(Hooks::default())); | ||
} | ||
} | ||
} | ||
|
||
pub fn init_config_mounts(&mut self) { | ||
self.init_config(); | ||
|
||
if let Some(spec) = self.config.as_mut() { | ||
if spec.mounts().is_none() { | ||
spec.set_mounts(Some(vec![Mount::default()])); | ||
} | ||
} | ||
} | ||
|
||
pub fn init_config_linux_intel_rdt(&mut self) { | ||
self.init_config_linux(); | ||
|
||
if let Some(linux) = self.config.as_mut().unwrap().linux_mut() { | ||
if linux.intel_rdt().is_none() { | ||
linux.set_intel_rdt(Some(LinuxIntelRdt::default())); | ||
} | ||
} | ||
} | ||
} |
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,253 @@ | ||
use std::cmp::Ordering; | ||
use std::path::PathBuf; | ||
|
||
use oci_spec::runtime::{Hook, LinuxDevice, LinuxDeviceCgroup, LinuxDeviceType, Mount}; | ||
|
||
use super::config::Generator; | ||
|
||
impl Generator { | ||
// remove_device removes a device from g.config.linux.devices | ||
pub fn remove_device(&mut self, path: &str) { | ||
self.init_config_linux(); | ||
if let Some(linux) = self.config.as_mut().unwrap().linux_mut() { | ||
if let Some(devices) = linux.devices_mut() { | ||
if let Some(index) = devices | ||
.iter() | ||
.position(|device| device.path() == &PathBuf::from(path)) | ||
{ | ||
devices.remove(index); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// add_device adds a device into g.config.linux.devices | ||
pub fn add_device(&mut self, device: LinuxDevice) { | ||
self.init_config_linux(); | ||
|
||
if let Some(linux) = self.config.as_mut().unwrap().linux_mut() { | ||
if let Some(devices) = linux.devices_mut() { | ||
if let Some(index) = devices.iter().position(|dev| dev.path() == device.path()) { | ||
devices[index] = device; | ||
} else { | ||
devices.push(device); | ||
} | ||
} else { | ||
linux.set_devices(Some(vec![device])); | ||
} | ||
} | ||
} | ||
|
||
// add_linux_resources_device adds a device into g.config.linux.resources.devices | ||
pub fn add_linux_resources_device( | ||
&mut self, | ||
allow: bool, | ||
dev_type: LinuxDeviceType, | ||
major: Option<i64>, | ||
minor: Option<i64>, | ||
access: Option<String>, | ||
) { | ||
self.init_config_linux_resources(); | ||
if let Some(linux) = self.config.as_mut().unwrap().linux_mut() { | ||
if let Some(resource) = linux.resources_mut() { | ||
if let Some(devices) = resource.devices_mut() { | ||
let mut device = LinuxDeviceCgroup::default(); | ||
device.set_allow(allow); | ||
device.set_typ(Some(dev_type)); | ||
device.set_major(major); | ||
device.set_minor(minor); | ||
device.set_access(access); | ||
|
||
devices.push(device); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// set Linux Intel RDT ClosID | ||
pub fn set_linux_intel_rdt_clos_id(&mut self, clos_id: String) { | ||
self.init_config_linux_intel_rdt(); | ||
if let Some(linux) = self.config.as_mut().unwrap().linux_mut() { | ||
if let Some(intel_rdt) = linux.intel_rdt_mut() { | ||
intel_rdt.set_clos_id(Some(clos_id)); | ||
} | ||
} | ||
} | ||
|
||
// add_process_additional_gid adds an additional gid into g.config.process.additional_gids. | ||
pub fn add_process_additional_gid(&mut self, gid: u32) { | ||
self.init_config_process(); | ||
if let Some(process) = self.config.as_mut().unwrap().process_mut() { | ||
if let Some(additional_gids) = process.user().additional_gids() { | ||
let mut tmp_vec = additional_gids.clone(); | ||
if !additional_gids.contains(&gid) { | ||
tmp_vec.push(gid) | ||
} | ||
|
||
process.user_mut().set_additional_gids(Some(tmp_vec)); | ||
} | ||
} | ||
} | ||
|
||
pub fn add_multiple_process_env(&mut self, envs: &[String]) { | ||
self.init_config_process(); | ||
|
||
if let Some(process) = self.config.as_mut().unwrap().process_mut() { | ||
let mut env_vec: Vec<String> = process.env_mut().get_or_insert_with(Vec::new).to_vec(); | ||
for env in envs { | ||
let split: Vec<&str> = env.splitn(2, '=').collect(); | ||
let key = split[0].to_string(); | ||
let idx = self.env_map.entry(key.clone()).or_insert(env_vec.len()); | ||
if let Some(elem) = env_vec.get_mut(*idx) { | ||
*elem = env.clone(); | ||
} else { | ||
env_vec.push(env.clone()); | ||
self.env_map.insert(key, env_vec.len() - 1); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// add_prestart_hook adds a prestart hook into g.config.hooks.prestart. | ||
pub fn add_prestart_hook(&mut self, hook: Hook) { | ||
self.init_config_hooks(); | ||
if let Some(hooks) = self.config.as_mut().unwrap().hooks_mut() { | ||
if let Some(prestart_hooks) = hooks.prestart_mut() { | ||
prestart_hooks.push(hook); | ||
} | ||
} | ||
} | ||
|
||
// add_poststop_hook adds a poststop hook into g.config.hooks.poststop. | ||
pub fn add_poststop_hook(&mut self, hook: Hook) { | ||
self.init_config_hooks(); | ||
if let Some(hooks) = self.config.as_mut().unwrap().hooks_mut() { | ||
if let Some(poststop_hooks) = hooks.poststop_mut() { | ||
poststop_hooks.push(hook); | ||
} | ||
} | ||
} | ||
|
||
// add_poststart_hook adds a poststart hook into g.config.hooks.poststart. | ||
pub fn add_poststart_hook(&mut self, hook: Hook) { | ||
self.init_config_hooks(); | ||
if let Some(hooks) = self.config.as_mut().unwrap().hooks_mut() { | ||
if let Some(poststart_hooks) = hooks.poststart_mut() { | ||
poststart_hooks.push(hook); | ||
} | ||
} | ||
} | ||
|
||
// add_createruntime_hook adds a create_runtime hook into g.config.hooks.create_runtime. | ||
pub fn add_createruntime_hook(&mut self, hook: Hook) { | ||
self.init_config_hooks(); | ||
if let Some(hooks) = self.config.as_mut().unwrap().hooks_mut() { | ||
if let Some(create_runtime) = hooks.create_runtime_mut() { | ||
create_runtime.push(hook); | ||
} | ||
} | ||
} | ||
|
||
// add_createcontainer_hook adds a create_container hook into g.config.hooks.create_container. | ||
pub fn add_createcontainer_hook(&mut self, hook: Hook) { | ||
self.init_config_hooks(); | ||
if let Some(hooks) = self.config.as_mut().unwrap().hooks_mut() { | ||
if let Some(create_container) = hooks.create_container_mut() { | ||
create_container.push(hook); | ||
} | ||
} | ||
} | ||
|
||
// add_start_container_hook adds a start container hook into g.config.hooks.start_container. | ||
pub fn add_startcontainer_hook(&mut self, hook: Hook) { | ||
self.init_config_hooks(); | ||
if let Some(hooks) = self.config.as_mut().unwrap().hooks_mut() { | ||
if let Some(start_container) = hooks.start_container_mut() { | ||
start_container.push(hook); | ||
} | ||
} | ||
} | ||
|
||
// remove_mount removes a mount point on the dest directory | ||
pub fn remove_mount(&mut self, dest: &str) { | ||
if let Some(mounts) = self.config.as_mut().unwrap().mounts_mut() { | ||
if let Some(index) = mounts | ||
.iter() | ||
.position(|m| m.destination() == &PathBuf::from(dest)) | ||
{ | ||
mounts.remove(index); | ||
} | ||
} | ||
} | ||
|
||
// add_mount adds a mount into g.config.mounts. | ||
pub fn add_mount(&mut self, mount: Mount) { | ||
self.init_config_mounts(); | ||
|
||
if let Some(mounts) = self.config.as_mut().unwrap().mounts_mut() { | ||
mounts.push(mount); | ||
} | ||
} | ||
|
||
// sort_mounts sorts the mounts in the given OCI Spec. | ||
pub fn sort_mounts(&mut self) { | ||
if let Some(ref mut mounts) = self.config.as_mut().unwrap().mounts_mut() { | ||
mounts.sort_by(|a, b| a.destination().cmp(b.destination())); | ||
} | ||
} | ||
|
||
// list_mounts returns the list of mounts | ||
pub fn list_mounts(&self) -> Option<&Vec<Mount>> { | ||
self.config.as_ref().and_then(|spec| spec.mounts().as_ref()) | ||
} | ||
|
||
// clear_mounts clear g.Config.Mounts | ||
pub fn clear_mounts(&mut self) { | ||
if let Some(spec) = self.config.as_mut() { | ||
spec.set_mounts(None); | ||
} | ||
} | ||
} | ||
|
||
// OrderedMounts defines how to sort an OCI Spec Mount slice. | ||
// This is the almost the same implementation sa used by CRI-O and Docker, | ||
// with a minor tweak for stable sorting order (easier to test): | ||
// | ||
// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26 | ||
struct OrderedMounts(Vec<Mount>); | ||
|
||
#[allow(dead_code)] | ||
impl OrderedMounts { | ||
fn new(mounts: Vec<Mount>) -> Self { | ||
OrderedMounts(mounts) | ||
} | ||
|
||
// parts returns the number of parts in the destination of a mount. Used in sorting. | ||
fn parts(&self, i: usize) -> usize { | ||
self.0[i].destination().components().count() | ||
} | ||
} | ||
|
||
impl Ord for OrderedMounts { | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
let self_parts = self.parts(0); | ||
let other_parts = other.parts(0); | ||
self_parts | ||
.cmp(&other_parts) | ||
.then_with(|| self.0[0].destination().cmp(other.0[0].destination())) | ||
} | ||
} | ||
|
||
impl PartialOrd for OrderedMounts { | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl PartialEq for OrderedMounts { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.parts(0) == other.parts(0) && self.0[0].destination() == other.0[0].destination() | ||
} | ||
} | ||
|
||
impl Eq for OrderedMounts {} |
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,7 @@ | ||
/// In cdi-go, the oci-runtime-tools generate tool is used to generate configuration JSON for the OCI runtime. | ||
/// However, since it is not possible to use existing libraries like cdi-go, we had to implement this functionality | ||
/// ourselves. Taking this opportunity, we hope to make this version the starting point for expanding oci-runtime-tools-rs | ||
/// and providing better support for the OCI runtime. | ||
/// It is important to note that at this stage, our primary focus is on implementations related to our cdi-rs project. | ||
pub mod config; | ||
pub mod generator; |
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