docker_api/api/
exec.rs

1//! Run new commands inside running containers.
2
3use hyper::Body;
4
5use crate::{
6    conn::{tty, Headers, Payload},
7    models,
8    opts::{ExecCreateOpts, ExecResizeOpts, ExecStartOpts},
9    stream, Docker, Result,
10};
11
12api_doc! { Exec
13/// Interface for docker exec instance
14|
15pub struct Exec {
16    docker: Docker,
17    id: crate::Id,
18}}
19
20impl Exec {
21    fn new(docker: Docker, id: impl Into<crate::Id>) -> Self {
22        Exec {
23            docker,
24            id: id.into(),
25        }
26    }
27
28    /// Get a reference to a set of operations available to an already created exec instance.
29    ///
30    /// It's in callers responsibility to ensure that exec instance with specified id actually
31    /// exists. Use [Exec::create](Exec::create) to ensure that the exec instance is created
32    /// beforehand.
33    pub fn get(docker: Docker, id: impl Into<crate::Id>) -> Exec {
34        Exec::new(docker, id)
35    }
36
37    api_doc! { Exec => Inspect
38    |
39    /// Inspect this Exec instance
40    pub async fn inspect(&self) -> Result<models::ExecInspect200Response> {
41        Self::inspect_impl(&self.docker, self.id.as_ref()).await
42    }}
43
44    async fn inspect_impl(docker: &Docker, id: &str) -> Result<models::ExecInspect200Response> {
45        docker.get_json(&format!("/exec/{id}/json")).await
46    }
47
48    async fn create_impl(
49        docker: Docker,
50        container_id: &str,
51        opts: &ExecCreateOpts,
52    ) -> Result<crate::Id> {
53        #[derive(serde::Deserialize)]
54        #[serde(rename_all = "PascalCase")]
55        struct Response {
56            id: String,
57        }
58
59        docker
60            .post_json(
61                &format!("/containers/{}/exec", container_id),
62                Payload::Json(opts.serialize_vec()?),
63                Headers::none(),
64            )
65            .await
66            .map(|resp: Response| resp.id.into())
67    }
68
69    api_doc! { Exec => Create
70    |
71    /// Creates a new exec instance that will be executed in a container with id == container_id.
72    pub async fn create(
73        docker: Docker,
74        container_id: impl AsRef<str>,
75        opts: &ExecCreateOpts,
76    ) -> Result<Exec>
77    {
78        Self::create_impl(docker.clone(), container_id.as_ref(), opts)
79        .await
80            .map(|id| Exec::new(docker, id))
81    }}
82
83    async fn start_impl(
84        docker: Docker,
85        id: &str,
86        opts: &ExecStartOpts,
87    ) -> Result<tty::Multiplexer> {
88        let endpoint = format!("/exec/{}/start", id);
89        let inspect_data = Self::inspect_impl(&docker, id).await?;
90        let is_tty = inspect_data
91            .process_config
92            .and_then(|c| c.tty)
93            .unwrap_or_default();
94
95        stream::attach(
96            docker,
97            endpoint,
98            Payload::Json(opts.serialize_vec()?.into()),
99            is_tty,
100        )
101        .await
102    }
103
104    api_doc! { Exec => Start
105    |
106    /// Starts this exec instance returning a multiplexed tty stream.
107    pub async fn start(&self, opts: &ExecStartOpts) -> Result<tty::Multiplexer> {
108        Self::start_impl(self.docker.clone(), self.id.as_ref(), opts).await
109    }}
110
111    pub(crate) async fn create_and_start(
112        docker: Docker,
113        container_id: impl AsRef<str>,
114        create_opts: &ExecCreateOpts,
115        start_opts: &ExecStartOpts,
116    ) -> Result<tty::Multiplexer> {
117        let container_id = container_id.as_ref();
118        let id = Self::create_impl(docker.clone(), container_id, create_opts).await?;
119
120        Self::start_impl(docker, id.as_ref(), start_opts).await
121    }
122
123    api_doc! { Exec => Resize
124    |
125    /// Resize the TTY session used by an exec instance. This only works if the exec was created
126    /// with `tty` enabled.
127    pub async fn resize(&self, opts: &ExecResizeOpts) -> Result<()> {
128        let body: Body = opts.serialize()?.into();
129
130        self.docker
131            .post_json(
132                &format!("/exec/{}/resize", &self.id),
133                Payload::Json(body),
134                Headers::none(),
135            )
136            .await
137    }}
138}