1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Represents a created container, in transit to become a OperationalContainer.

use crate::{
    composition::{LogOptions, StaticManagementPolicy},
    container::OperationalContainer,
    docker::{ContainerState, Docker},
    waitfor::WaitFor,
    DockerTestError, StartPolicy,
};

/// Represent a docker container object in a pending phase between
/// it being created on the daemon, but may not be running.
///
/// This object is an implementation detail of `dockertest-rs` and is only
/// publicly exposed due to the public `WaitFor` trait which is responsible
/// of performing the into conversion from `PendingContainer` to `OperationalContainer`.
// NOTE: No methods on this structure, nor fields, shall be publicly exposed.
#[derive(Clone)]
pub struct PendingContainer {
    /// The docker client
    pub(crate) client: Docker,

    /// Name of the container, defaults to the repository name of the image.
    pub(crate) name: String,

    /// Id of the running container.
    pub(crate) id: String,

    /// Handle used to interact with the container from the user
    pub(crate) handle: String,

    /// The StartPolicy of this Container, is provided from its Composition.
    pub(crate) start_policy: StartPolicy,

    /// Trait implementing how to wait for the container to startup.
    pub(crate) wait: Option<Box<dyn WaitFor>>,

    /// Wheter this is a static container
    pub(crate) is_static: bool,

    /// The StaticManagementPolicy of this container if any exists
    pub(crate) static_management_policy: Option<StaticManagementPolicy>,

    /// Container log options, they are provided by `Composition`.
    pub(crate) log_options: Option<LogOptions>,

    /// The containers' expected state after executing its `WaitFor` implementation.
    pub(crate) expected_state: ContainerState,
}

impl PendingContainer {
    /// Creates a new Container object with the given values.
    // FIXME(veeg): reword the PendingContainer API to be more ergonomic
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn new<T: ToString, R: ToString, H: ToString>(
        name: T,
        id: R,
        handle: H,
        start_policy: StartPolicy,
        wait: Box<dyn WaitFor>,
        client: Docker,
        static_management_policy: Option<StaticManagementPolicy>,
        log_options: Option<LogOptions>,
    ) -> PendingContainer {
        PendingContainer {
            client,
            name: name.to_string(),
            id: id.to_string(),
            handle: handle.to_string(),
            start_policy,
            is_static: static_management_policy.is_some(),
            static_management_policy,
            log_options,
            expected_state: wait.expected_state(),
            wait: Some(wait),
        }
    }

    /// Run the start command and initiate the WaitFor condition.
    /// Once the PendingContainer is successfully started and the WaitFor condition
    /// has been achived, the OperationalContainer is returned.
    pub(crate) async fn start(self) -> Result<OperationalContainer, DockerTestError> {
        // TODO: dont clone
        let client = self.client.clone();
        client.start_container(self).await
    }

    // Internal start method should only be invoked from the static mod.
    // TODO: isolate to static mod only
    pub(crate) async fn start_inner(self) -> Result<OperationalContainer, DockerTestError> {
        // TODO: dont clone
        let client = self.client.clone();
        client.start_container_inner(self).await
    }
}

#[cfg(test)]
mod tests {
    use crate::container::PendingContainer;
    use crate::docker::Docker;
    use crate::waitfor::NoWait;
    use crate::StartPolicy;

    /// Tests `PendingContainer::new` with associated struct member field values.
    #[tokio::test]
    async fn test_new_pending_container() {
        let client = Docker::new().unwrap();
        let id = "this_is_an_id".to_string();
        let name = "this_is_a_container_name".to_string();
        let handle_key = "this_is_a_handle_key";

        let container = PendingContainer::new(
            &name,
            &id,
            handle_key,
            StartPolicy::Relaxed,
            Box::new(NoWait {}),
            client,
            None,
            None,
        );
        assert_eq!(id, container.id, "wrong id set in container creation");
        assert_eq!(name, container.name, "wrong name set in container creation");
        assert_eq!(
            name, container.name,
            "container name getter returns wrong value"
        );
        assert_eq!(
            handle_key, container.handle,
            "wrong handle_key set in container creation"
        );
    }
}