| 166 | } |
| 167 | |
| 168 | pub async fn load_vm( |
| 169 | &self, |
| 170 | work_dir: impl AsRef<Path>, |
| 171 | cids_assigned: &HashMap<String, u32>, |
| 172 | auto_start: bool, |
| 173 | ) -> Result<()> { |
| 174 | let vm_work_dir = VmWorkDir::new(work_dir.as_ref()); |
| 175 | let manifest = vm_work_dir.manifest().context("Failed to read manifest")?; |
| 176 | if manifest.image.len() > 64 |
| 177 | || manifest.image.contains("..") |
| 178 | || !manifest |
| 179 | .image |
| 180 | .chars() |
| 181 | .all(|c| c.is_alphanumeric() || c == '_' || c == '-' || c == '.') |
| 182 | { |
| 183 | bail!("Invalid image name"); |
| 184 | } |
| 185 | let image_path = self.config.image.path.join(&manifest.image); |
| 186 | let image = Image::load(&image_path).context("Failed to load image")?; |
| 187 | let vm_id = manifest.id.clone(); |
| 188 | let app_compose = vm_work_dir |
| 189 | .app_compose() |
| 190 | .context("Failed to read compose file")?; |
| 191 | { |
| 192 | let mut states = self.lock(); |
| 193 | let cid = states |
| 194 | .get(&vm_id) |
| 195 | .map(|vm| vm.config.cid) |
| 196 | .or_else(|| cids_assigned.get(&vm_id).cloned()) |
| 197 | .or_else(|| states.cid_pool.allocate()) |
| 198 | .context("CID pool exhausted")?; |
| 199 | let vm_config = VmConfig { |
| 200 | manifest, |
| 201 | image, |
| 202 | cid, |
| 203 | workdir: vm_work_dir.path().to_path_buf(), |
| 204 | gateway_enabled: app_compose.gateway_enabled(), |
| 205 | }; |
| 206 | match states.get_mut(&vm_id) { |
| 207 | Some(vm) => { |
| 208 | vm.config = vm_config.into(); |
| 209 | } |
| 210 | None => { |
| 211 | states.add(VmState::new(vm_config)); |
| 212 | } |
| 213 | } |
| 214 | }; |
| 215 | if auto_start && vm_work_dir.started().unwrap_or_default() { |
| 216 | self.start_vm(&vm_id).await?; |
| 217 | } |
| 218 | Ok(()) |
| 219 | } |
| 220 | |
| 221 | pub async fn start_vm(&self, id: &str) -> Result<()> { |
| 222 | { |