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
use std::{
    collections::HashMap,
    sync::{Arc, LazyLock},
};

use crate::{docker::Docker, DockerTestError};
use tokio::sync::RwLock;

static SINGULAR_NETWORK_NAME: &str = "dockertest";

// Controls all interaction with scoped networks within a single test binary
pub(crate) static SCOPED_NETWORKS: LazyLock<ScopedNetworks> =
    LazyLock::new(ScopedNetworks::default);

#[derive(Default)]
pub struct ScopedNetworks {
    singular: Arc<RwLock<HashMap<String, SingularNetwork>>>,
}

#[derive(Debug)]
pub struct SingularNetwork {
    status: NetworkCreation,
}

// What state the singular network is currently in
#[derive(Debug)]
enum NetworkCreation {
    Failed,
    Complete(String),
}

impl ScopedNetworks {
    pub(crate) fn name(&self, namespace: &str) -> String {
        format!("{namespace}-{SINGULAR_NETWORK_NAME}")
    }
}

impl ScopedNetworks {
    pub(crate) async fn create_singular_network(
        &self,
        client: &Docker,
        self_container: Option<&str>,
        namespace: &str,
    ) -> Result<String, DockerTestError> {
        let mut networks = self.singular.write().await;

        let network_name = self.name(namespace);

        if let Some(network) = networks.get(namespace) {
            match &network.status {
                NetworkCreation::Failed => Err(DockerTestError::Startup(
                    "failed to create singular network".to_string(),
                )),
                NetworkCreation::Complete(id) => Ok(id.clone()),
            }
        } else {
            let id = if let Some(id) = client.existing_dockertest_network(&network_name).await? {
                networks.insert(
                    namespace.to_string(),
                    SingularNetwork {
                        status: NetworkCreation::Complete(id.clone()),
                    },
                );
                return Ok(id);
            } else {
                match client.create_singular_network_impl(network_name).await {
                    Ok(id) => Ok(id),
                    Err(e) => {
                        networks.insert(
                            namespace.to_string(),
                            SingularNetwork {
                                status: NetworkCreation::Failed,
                            },
                        );
                        Err(e)
                    }
                }
            }?;
            if let Some(container_id) = self_container {
                if let Err(e) = client.add_self_to_network(container_id, &id).await {
                    networks.insert(
                        namespace.to_string(),
                        SingularNetwork {
                            status: NetworkCreation::Failed,
                        },
                    );
                    return Err(e);
                }
            }
            networks.insert(
                namespace.to_string(),
                SingularNetwork {
                    status: NetworkCreation::Complete(id.to_string()),
                },
            );
            Ok(id)
        }
    }
}