Skip to main content

libcontainer/container/
container_delete.rs

1use std::fs;
2
3use libcgroups::common::CgroupManager;
4use libcgroups::{self};
5use nix::sys::signal;
6
7use super::{Container, ContainerStatus};
8use crate::config::YoukiConfig;
9use crate::error::LibcontainerError;
10use crate::hooks;
11use crate::process::intel_rdt::delete_resctrl_subdirectory;
12
13impl Container {
14    /// Deletes the container
15    ///
16    /// # Example
17    ///
18    /// ```no_run
19    /// use libcontainer::container::builder::ContainerBuilder;
20    /// use libcontainer::syscall::syscall::SyscallType;
21    ///
22    /// # fn main() -> anyhow::Result<()> {
23    /// let mut container = ContainerBuilder::new(
24    ///     "74f1a4cb3801".to_owned(),
25    ///     SyscallType::default(),
26    /// )
27    /// .as_init("/var/run/docker/bundle")
28    /// .build()?;
29    ///
30    /// container.delete(true)?;
31    /// # Ok(())
32    /// # }
33    /// ```
34    pub fn delete(&mut self, force: bool) -> Result<(), LibcontainerError> {
35        self.refresh_status()?;
36
37        tracing::debug!("container status: {:?}", self.status());
38
39        // Check if container is allowed to be deleted based on container status.
40        match self.status() {
41            ContainerStatus::Stopped => {}
42            ContainerStatus::Created => {
43                // Here, we differ from the OCI spec, but matches the same
44                // behavior as `runc` and `crun`. The OCI spec does not allow
45                // deletion of status `created` without `force` flag. But both
46                // `runc` and `crun` allows deleting `created`. Therefore we
47                // decided to follow `runc` and `crun`.
48                self.do_kill(signal::Signal::SIGKILL, true)?;
49                self.set_status(ContainerStatus::Stopped).save()?;
50            }
51            ContainerStatus::Creating | ContainerStatus::Running | ContainerStatus::Paused => {
52                // Containers can't be deleted while in these status, unless
53                // force flag is set. In the force case, we need to clean up any
54                // processes associated with containers.
55                if force {
56                    self.do_kill(signal::Signal::SIGKILL, true)?;
57                    self.set_status(ContainerStatus::Stopped).save()?;
58                } else {
59                    tracing::error!(
60                        id = ?self.id(),
61                        status = ?self.status(),
62                        "delete requires the container state to be stopped or created",
63                    );
64                    return Err(LibcontainerError::IncorrectStatus(self.status()));
65                }
66            }
67        }
68
69        // Once reached here, the container is verified that it can be deleted.
70        debug_assert!(self.status().can_delete());
71
72        if let Some(true) = &self.clean_up_intel_rdt_subdirectory() {
73            if let Err(err) = delete_resctrl_subdirectory(self.id()) {
74                tracing::warn!(
75                    "failed to delete resctrl subdirectory due to: {err:?}, continue to delete"
76                );
77            }
78        }
79
80        if self.root.exists() {
81            match YoukiConfig::load(&self.root) {
82                Ok(config) => {
83                    tracing::debug!("config: {:?}", config);
84
85                    // remove the cgroup created for the container
86                    // check https://man7.org/linux/man-pages/man7/cgroups.7.html
87                    // creating and removing cgroups section for more information on cgroups
88                    let cmanager = libcgroups::common::create_cgroup_manager(
89                        libcgroups::common::CgroupConfig {
90                            cgroup_path: config.cgroup_path.to_owned(),
91                            systemd_cgroup: self.systemd(),
92                            container_name: self.id().to_string(),
93                        },
94                    )?;
95                    cmanager.remove().map_err(|err| {
96                        tracing::error!(cgroup_path = ?config.cgroup_path, "failed to remove cgroup due to: {err:?}");
97                        err
98                    })?;
99
100                    if let Some(hooks) = config.hooks.as_ref() {
101                        hooks::run_hooks(hooks.poststop().as_ref(), Some(&self.state), None, None)
102                            .map_err(|err| {
103                                tracing::error!(err = ?err, "failed to run post stop hooks");
104                                err
105                            })?;
106                    }
107                }
108                Err(err) => {
109                    // There is a brief window where the container state is
110                    // created, but the container config is not yet generated
111                    // from the OCI spec. In this case, we assume as if we
112                    // successfully deleted the config and moving on.
113                    tracing::warn!(
114                        "skipping loading youki config due to: {err:?}, continue to delete"
115                    );
116                }
117            }
118
119            // remove the directory storing container state
120            tracing::debug!("remove dir {:?}", self.root);
121            fs::remove_dir_all(&self.root).map_err(|err| {
122                tracing::error!(?err, path = ?self.root, "failed to remove container dir");
123                LibcontainerError::OtherIO(err)
124            })?;
125        }
126
127        Ok(())
128    }
129}