rs_docker_api_rs/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<rs_docker_api_stubs::models::ExecInspect200Response> {
41        Self::inspect_impl(&self.docker, self.id.as_ref()).await
42    }}
43
44    async fn inspect_impl(
45        docker: &Docker,
46        id: &str,
47    ) -> Result<rs_docker_api_stubs::models::ExecInspect200Response> {
48        docker.get_json(&format!("/exec/{id}/json")).await
49    }
50
51    async fn create_impl(
52        docker: Docker,
53        container_id: &str,
54        opts: &ExecCreateOpts,
55    ) -> Result<crate::Id> {
56        #[derive(serde::Deserialize)]
57        #[serde(rename_all = "PascalCase")]
58        struct Response {
59            id: String,
60        }
61
62        docker
63            .post_json(
64                &format!("/containers/{}/exec", container_id),
65                Payload::Json(opts.serialize_vec()?),
66                Headers::none(),
67            )
68            .await
69            .map(|resp: Response| resp.id.into())
70    }
71
72    api_doc! { Exec => Create
73    |
74    /// Creates a new exec instance that will be executed in a container with id == container_id.
75    pub async fn create(
76        docker: Docker,
77        container_id: impl AsRef<str>,
78        opts: &ExecCreateOpts,
79    ) -> Result<Exec>
80    {
81        Self::create_impl(docker.clone(), container_id.as_ref(), opts)
82        .await
83            .map(|id| Exec::new(docker, id))
84    }}
85
86    async fn start_impl(
87        docker: Docker,
88        id: &str,
89        opts: &ExecStartOpts,
90    ) -> Result<tty::Multiplexer> {
91        let endpoint = format!("/exec/{}/start", id);
92        let inspect_data = Self::inspect_impl(&docker, id).await?;
93        let is_tty = inspect_data
94            .process_config
95            .and_then(|c| c.tty)
96            .unwrap_or_default();
97
98        stream::attach(
99            docker,
100            endpoint,
101            Payload::Json(opts.serialize_vec()?.into()),
102            is_tty,
103        )
104        .await
105    }
106
107    api_doc! { Exec => Start
108    |
109    /// Starts this exec instance returning a multiplexed tty stream.
110    pub async fn start(&self, opts: &ExecStartOpts) -> Result<tty::Multiplexer> {
111        Self::start_impl(self.docker.clone(), self.id.as_ref(), opts).await
112    }}
113
114    pub(crate) async fn create_and_start(
115        docker: Docker,
116        container_id: impl AsRef<str>,
117        create_opts: &ExecCreateOpts,
118        start_opts: &ExecStartOpts,
119    ) -> Result<tty::Multiplexer> {
120        let container_id = container_id.as_ref();
121        let id = Self::create_impl(docker.clone(), container_id, create_opts).await?;
122
123        Self::start_impl(docker, id.as_ref(), start_opts).await
124    }
125
126    api_doc! { Exec => Resize
127    |
128    /// Resize the TTY session used by an exec instance. This only works if the exec was created
129    /// with `tty` enabled.
130    pub async fn resize(&self, opts: &ExecResizeOpts) -> Result<()> {
131        let body: Body = opts.serialize()?.into();
132
133        self.docker
134            .post_json(
135                &format!("/exec/{}/resize", &self.id),
136                Payload::Json(body),
137                Headers::none(),
138            )
139            .await
140    }}
141}