| 662 | } |
| 663 | |
| 664 | async fn delete_image(self, request: Id) -> Result<()> { |
| 665 | let name = &request.id; |
| 666 | if name.is_empty() || name.contains("..") || name.contains('/') { |
| 667 | bail!("invalid image name"); |
| 668 | } |
| 669 | |
| 670 | // Check no VM uses this image |
| 671 | { |
| 672 | let state = self.app.lock(); |
| 673 | for vm in state.iter_vms() { |
| 674 | if vm.config.manifest.image == *name { |
| 675 | bail!( |
| 676 | "cannot delete image '{}': in use by VM '{}'", |
| 677 | name, |
| 678 | vm.config.manifest.name, |
| 679 | ); |
| 680 | } |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | let image_dir = self.app.config.image.path.join(name); |
| 685 | if !image_dir.exists() { |
| 686 | bail!("image '{}' not found", name); |
| 687 | } |
| 688 | |
| 689 | fs_err::remove_dir_all(&image_dir).with_context(|| { |
| 690 | format!("failed to delete image directory: {}", image_dir.display()) |
| 691 | })?; |
| 692 | |
| 693 | info!("deleted local image: {name}"); |
| 694 | Ok(()) |
| 695 | } |
| 696 | |
| 697 | async fn pull_registry_image(self, request: PullRegistryImageRequest) -> Result<()> { |
| 698 | let registry = &self.app.config.image.registry; |