wasmcloud_provider_sqldb_postgres/
config.rs

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
use tracing::warn;
use wasmcloud_provider_sdk::{core::secrets::SecretValue, LinkConfig};

const POSTGRES_DEFAULT_PORT: u16 = 5432;

/// Creation options for a Postgres connection
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ConnectionCreateOptions {
    /// Hostname of the Postgres cluster to connect to
    pub host: String,
    /// Port on which to connect to the Postgres cluster
    pub port: u16,
    /// Username used when accessing the Postgres cluster
    pub username: String,
    /// Password used when accessing the Postgres cluster
    pub password: String,
    /// Database to connect to
    pub database: String,
    /// Whether TLS is required for the connection
    pub tls_required: bool,
    /// Optional connection pool size
    pub pool_size: Option<usize>,
}

impl From<ConnectionCreateOptions> for deadpool_postgres::Config {
    fn from(opts: ConnectionCreateOptions) -> Self {
        let mut cfg = deadpool_postgres::Config::new();
        cfg.host = Some(opts.host);
        cfg.user = Some(opts.username);
        cfg.password = Some(opts.password);
        cfg.dbname = Some(opts.database);
        cfg.port = Some(opts.port);
        if let Some(pool_size) = opts.pool_size {
            cfg.pool = Some(deadpool_postgres::PoolConfig {
                max_size: pool_size,
                ..deadpool_postgres::PoolConfig::default()
            });
        }
        cfg
    }
}

/// Parse the options for Postgres configuration from a [`HashMap`], with a given prefix to the keys
///
/// For example given a prefix like `EXAMPLE_`, and a Hashmap that contains an entry like ("EXAMPLE_HOST", "localhost"),
/// the parsed [`ConnectionCreateOptions`] would contain "localhost" as the host.
pub(crate) fn extract_prefixed_conn_config(
    prefix: &str,
    link_config: &LinkConfig,
) -> Option<ConnectionCreateOptions> {
    let LinkConfig {
        config, secrets, ..
    } = link_config;

    let keys = [
        format!("{prefix}HOST"),
        format!("{prefix}PORT"),
        format!("{prefix}USERNAME"),
        format!("{prefix}PASSWORD"),
        format!("{prefix}DATABASE"),
        format!("{prefix}TLS_REQUIRED"),
        format!("{prefix}POOL_SIZE"),
    ];
    match keys
        .iter()
        .map(|k| config.get(k))
        .collect::<Vec<Option<&String>>>()[..]
    {
        [Some(host), Some(port), Some(username), config_password, Some(database), tls_required, pool_size] =>
        {
            let secret_password = secrets
                .get(&format!("{prefix}PASSWORD"))
                .and_then(SecretValue::as_string);
            // Check that the password was pulled from secrets, not config
            let password = match (secret_password, config_password) {
                (Some(s), _) => s,
                (None, Some(c)) => {
                    warn!("secret value [{prefix}PASSWORD] was not found in secrets, but exists in config. Prefer using secrets for sensitive values.", );
                    c
                }
                (_, None) => {
                    warn!("failed to find password in config and secrets");
                    return None;
                }
            };

            let pool_size = pool_size.and_then(|pool_size| {
                pool_size.as_str().parse::<usize>().ok().or_else(|| {
                    warn!("invalid pool size value [{pool_size}], using default");
                    None
                })
            });

            Some(ConnectionCreateOptions {
                host: host.to_string(),
                port: port.parse::<u16>().unwrap_or_else(|_e| {
                    warn!("invalid port value [{port}], using {POSTGRES_DEFAULT_PORT}");
                    POSTGRES_DEFAULT_PORT
                }),
                username: username.to_string(),
                password: password.to_string(),
                tls_required: tls_required.is_some_and(|tls_required| {
                    matches!(tls_required.to_lowercase().as_str(), "true" | "yes")
                }),
                database: database.to_string(),
                pool_size,
            })
        }
        _ => {
            warn!("failed to find keys in configuration: [{:?}]", keys);
            None
        }
    }
}