dockertest/lib.rs
1#![deny(missing_docs)]
2#![deny(warnings)]
3#![deny(rust_2018_idioms)]
4#![deny(rustdoc::broken_intra_doc_links)]
5
6//! _dockertest_ is a testing and automation abstraction for Docker.
7//!
8//! This testing library enables control and lifecycle management with docker containers
9//! from an integration test, taking full care of start and cleanup phases. Containers can
10//! be backed by various container registries, even behind authentication barrier.
11//!
12//! The main entrypoint to construct and a run a dockertest powered integration test starts
13//! with [DockerTest].
14//!
15//! The underlying docker engine to use can be configured through:
16//! - UNIX domain socket (linux)
17//! - Named pipes (windows)
18//! - TCP with TLS
19//! - Piped through a docker-in-docker container where the execution occurs, to run on the
20//! underlying docker engine.
21//!
22//! The main bread-and-butter of this library is the ability to specify which containers are
23//! required for a test, and how one should ensure that the container is properly running prior
24//! to starting the actual test body. Furthermore, dockertest supports interacting with existing
25//! containers, without interfering with its lifecycle management.
26//!
27//! ## Lifecycle management
28//!
29//! There are multiple ways to control and interact with containers, with various trade-offs.
30//!
31//! ### Full isolation - `TestBodySpecification`
32//!
33//! The container is created, started, and cleaned up entirely within a single test body. This is
34//! the highest level of isolation, and no other tests can interfere with this container. The main
35//! drawback of this method is the high wall-time needed to start these containers. If there are
36//! many integration tests in this mode, the total test run-time will be made primarily by the
37//! container management overhead.
38//!
39//! ### Externally managed - `ExternalSpecification`
40//!
41//! The container is _never_ touched by dockertest. It is assumed to be started by some external
42//! entity, and the container is never teared down by dockertest. Prior to running the test body,
43//! the existence of the external container is verified.
44//!
45//! ### Conditionally managed - `DynamicSpecification`
46//!
47//! The containers lifecycle is only managed for creation, but not deletion. When multiple
48//! specifications relate to the same underlying container, dockertest will attempt to locate
49//! an existing container, and reuse this container to fulfill the specification. Once the
50//! test body exists, the container will be left running.
51//!
52//! This mode is useful in when there are multiple tests, over a long period of development time,
53//! that can utilize the same underlying container without causing cross-test contamination.
54//! This will lead to significantly faster test execution time.
55//!
56//! # `WaitFor` - determining when a container is ready
57//!
58//! Each container that dockertest creates and starts must also have a policy to detect
59//! when the container it has started is actually ready to start serving the test body.
60//! This is achived through a trait object [WaitFor], where an implementation can be provided
61//! outside of dockertest.
62//!
63//! The batteries included implementations are:
64//! * [RunningWait] - wait for the container to report _running_ status.
65//! * [ExitedWait] - wait for the container to report _exited_ status.
66//! * [NoWait] - don't wait for anything
67//! * [MessageWait] - wait for the following message to appear in the log stream.
68//!
69//! # Environment variables
70//!
71//! The following set of environment variables can impact running tests utilizing dockertest.
72//!
73//! ## Prune policy
74//!
75//! By default, dockertest will stop and remove all containers and created volumes
76//! regardless of execution result. You can control this policy by setting the environment variable
77//! `DOCKERTEST_PRUNE`:
78//! * `always`: default remove everything
79//! * `never`: leave all containers running
80//! * `stop_on_failure`: stop containers on execution failure
81//! * `running_on_failure`: leave containers running on execution failure
82//!
83//! ## Dockertest in Docker
84//!
85//! If the execution environment of running dockertest is itself a docker-in-docker container, one
86//! may have connectivity issues between the test body code, and the container dependencies.
87//! To ensure connectivity, dockertest must be instructed about the name/identifier of the
88//! execution container to include it into the docker network of the test containers.
89//! This is usually because the docker-in-docker docker daemon connection is routed to
90//! the underlying host itself.
91//!
92//! `DOCKERTEST_CONTAINER_ID_INJECT_TO_NETWORK=your_container_id/name`
93//!
94//! # Example
95//!
96//! ```no_run
97//! use dockertest::{TestBodySpecification, DockerTest};
98//! use std::sync::{Arc, Mutex};
99//!
100//! #[test]
101//! fn hello_world_test() {
102//! // Define our test instance
103//! let mut test = DockerTest::new();
104//!
105//! // A container specification can have multiple properties, depending on how the
106//! // lifecycle management of the container should be handled by dockertest.
107//! //
108//! // For any container specification where dockertest needs to create and start the container,
109//! // we must provide enough information to construct a composition of
110//! // an Image configured with provided environment variables, arguments, StartPolicy, etc.
111//! let hello = TestBodySpecification::with_repository("hello-world");
112//!
113//! // Populate the test instance.
114//! // The order of compositions added reflect the execution order (depending on StartPolicy).
115//! test.provide_container(hello);
116//!
117//! let has_ran: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
118//! let has_ran_test = has_ran.clone();
119//! test.run(|ops| async move {
120//! // A handle to operate on the Container.
121//! let _container = ops.handle("hello-world");
122//!
123//! // The container is in a running state at this point.
124//! // Depending on the Image, it may exit on its own (like this hello-world image)
125//! let mut ran = has_ran_test.lock().unwrap();
126//! *ran = true;
127//! });
128//!
129//! let ran = has_ran.lock().unwrap();
130//! assert!(*ran);
131//! }
132//! ```
133//!
134//! [WaitFor]: crate::waitfor::WaitFor
135//! [RunningWait]: crate::waitfor::RunningWait
136//! [ExitedWait]: crate::waitfor::ExitedWait
137//! [NoWait]: crate::waitfor::NoWait
138//! [MessageWait]: crate::waitfor::MessageWait
139
140mod composition;
141mod container;
142mod docker;
143mod dockertest;
144mod engine;
145mod error;
146mod image;
147mod runner;
148mod specification;
149mod static_container;
150// We only make this public because a function is used in our integration test
151#[doc(hidden)]
152pub mod utils;
153pub mod waitfor;
154
155pub use crate::composition::{LogAction, LogOptions, LogPolicy, LogSource, StartPolicy};
156pub use crate::container::{OperationalContainer, PendingContainer};
157pub use crate::docker::ContainerState;
158pub use crate::dockertest::DockerTest;
159pub use crate::dockertest::Network;
160pub use crate::error::DockerTestError;
161pub use crate::image::{Image, PullPolicy, RegistryCredentials, Source};
162pub use crate::runner::DockerOperations;
163pub use crate::specification::{
164 ContainerSpecification, DynamicSpecification, ExternalSpecification, TestBodySpecification,
165 TestSuiteSpecification,
166};