1 // Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. 2 // 3 // StratoVirt is licensed under Mulan PSL v2. 4 // You can use this software according to the terms and conditions of the Mulan 5 // PSL v2. 6 // You may obtain a copy of Mulan PSL v2 at: 7 // http://license.coscl.org.cn/MulanPSL2 8 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY 9 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 10 // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 // See the Mulan PSL v2 for more details. 12 13 use std::{collections::HashMap, fs::File, io::BufReader, path::Path}; 14 15 use anyhow::{anyhow, Context, Result}; 16 use serde::{Deserialize, Serialize}; 17 18 #[cfg(target_os = "linux")] 19 use crate::linux::IdMapping; 20 #[cfg(target_family = "unix")] 21 use crate::posix::Root; 22 use crate::{linux::LinuxPlatform, posix::Hooks, process::Process, vm::VmPlatform}; 23 24 /// Additional mounts beyond root. 25 #[allow(non_snake_case)] 26 #[derive(Serialize, Deserialize, Debug, Clone)] 27 pub struct Mount { 28 /// Destination of mount point: path inside container. 29 pub destination: String, 30 /// A device name, but can also be a file or directory name for bind mounts 31 /// or a dummy. 32 #[serde(skip_serializing_if = "Option::is_none")] 33 pub source: Option<String>, 34 /// Mount options of the filesystem to be used. 35 #[serde(skip_serializing_if = "Option::is_none")] 36 pub options: Option<Vec<String>>, 37 /// The type of the filesystem to be mounted. 38 #[serde(skip_serializing_if = "Option::is_none", rename = "type")] 39 pub fs_type: Option<String>, 40 /// The mapping to convert UIDs from the source file system to the 41 /// destination mount point. 42 #[serde(skip_serializing_if = "Option::is_none")] 43 pub uidMappings: Option<IdMapping>, 44 /// The mapping to convert GIDs from the source file system to the 45 /// destination mount point. 46 #[serde(skip_serializing_if = "Option::is_none")] 47 pub gidMappings: Option<IdMapping>, 48 } 49 50 /// Metadata necessary to implement standard operations against the container. 51 #[allow(non_snake_case)] 52 #[derive(Serialize, Deserialize, Debug, Clone)] 53 pub struct RuntimeConfig { 54 /// Version of the Open Container Initiative Runtime Specification 55 /// with which the bundle complies. 56 pub ociVersion: String, 57 /// Container's root filesystem. 58 pub root: Root, 59 /// Additional mounts beyond root. 60 pub mounts: Vec<Mount>, 61 /// Container process. 62 pub process: Process, 63 /// Container's hostname as seen by processes running inside the container. 64 #[serde(skip_serializing_if = "Option::is_none")] 65 pub hostname: Option<String>, 66 /// Container's domainname as seen by processes running inside the 67 /// container. 68 #[serde(skip_serializing_if = "Option::is_none")] 69 pub domainname: Option<String>, 70 /// Linux-specific section of the container configuration. 71 #[cfg(target_os = "linux")] 72 #[serde(skip_serializing_if = "Option::is_none")] 73 pub linux: Option<LinuxPlatform>, 74 /// Vm-specific section of the container configuration. 75 #[serde(skip_serializing_if = "Option::is_none")] 76 pub vm: Option<VmPlatform>, 77 /// Custom actions related to the lifecycle of the container. 78 #[cfg(target_family = "unix")] 79 #[serde(skip_serializing_if = "Option::is_none")] 80 pub hooks: Option<Hooks>, 81 /// Arbitrary metadata for the container. 82 #[serde(skip_serializing_if = "Option::is_none")] 83 pub annotations: Option<HashMap<String, String>>, 84 } 85 86 impl RuntimeConfig { from_file(path: &String) -> Result<RuntimeConfig>87 pub fn from_file(path: &String) -> Result<RuntimeConfig> { 88 let file = File::open(Path::new(path)).with_context(|| "Failed to open config.json")?; 89 let reader = BufReader::new(file); 90 serde_json::from_reader(reader).map_err(|e| anyhow!("Failed to load config.json: {:?}", e)) 91 } 92 } 93 94 #[cfg(test)] 95 mod tests { 96 use super::*; 97 use serde_json; 98 99 #[test] test_mounts()100 fn test_mounts() { 101 let json = r#"{ 102 "mounts": [ 103 { 104 "destination": "/proc", 105 "type": "proc", 106 "source": "proc" 107 }, 108 { 109 "destination": "/dev", 110 "type": "tmpfs", 111 "source": "tmpfs", 112 "options": [ 113 "nosuid", 114 "strictatime", 115 "mode=755", 116 "size=65536k" 117 ] 118 } 119 ] 120 }"#; 121 122 #[allow(non_snake_case)] 123 #[derive(Serialize, Deserialize)] 124 struct Section { 125 mounts: Vec<Mount>, 126 } 127 128 let section: Section = serde_json::from_str(json).unwrap(); 129 assert_eq!(section.mounts.len(), 2); 130 assert_eq!(section.mounts[0].destination, "/proc"); 131 assert_eq!(section.mounts[0].fs_type, Some("proc".to_string())); 132 assert_eq!(section.mounts[0].source, Some("proc".to_string())); 133 let options = section.mounts[1].options.as_ref().unwrap(); 134 assert_eq!(options.len(), 4); 135 assert_eq!(options[0], "nosuid"); 136 assert_eq!(options[1], "strictatime"); 137 assert_eq!(options[2], "mode=755"); 138 assert_eq!(options[3], "size=65536k"); 139 } 140 } 141