golem-cli 0.0.23

Command line interface for OSS version of Golem. See also golem-cloud-cli.
Documentation
use crate::context::{DbType, EnvConfig, NETWORK};
use libtest_mimic::Failed;
use std::collections::HashMap;
use std::path::PathBuf;
use testcontainers::{clients, Container, RunnableImage};

pub struct Db<'docker_client> {
    inner: DbInner<'docker_client>,
}

pub enum DbInner<'docker_client> {
    Sqlite(PathBuf),
    Postgres {
        host: String,
        port: u16,
        local_port: u16,
        _node: Container<'docker_client, testcontainers_modules::postgres::Postgres>,
    },
}

impl<'docker_client> Db<'docker_client> {
    fn test_started(local_port: u16) -> Result<(), Failed> {
        let mut conn =
            ::postgres::Client::connect(&Db::connection_string(local_port), ::postgres::NoTls)
                .unwrap();

        let _ = conn.query("SELECT version()", &[])?;
        Ok(())
    }

    pub fn start(
        docker: &'docker_client clients::Cli,
        env_config: &EnvConfig,
    ) -> Result<Db<'docker_client>, Failed> {
        match &env_config.db_type {
            DbType::Sqlite => Db::prepare_sqlite(),
            DbType::Postgres => Db::start_postgres(docker, env_config),
        }
    }

    fn prepare_sqlite() -> Result<Db<'docker_client>, Failed> {
        let path = PathBuf::from("../target/golem_test_db");

        if path.exists() {
            std::fs::remove_file(&path)?
        }

        Ok(Db {
            inner: DbInner::Sqlite(path),
        })
    }

    fn start_postgres(
        docker: &'docker_client clients::Cli,
        env_config: &EnvConfig,
    ) -> Result<Db<'docker_client>, Failed> {
        println!("Starting Postgres in docker");

        let name = "golem_postgres";
        let image = RunnableImage::from(testcontainers_modules::postgres::Postgres::default())
            .with_tag("12");

        let image = if env_config.local_golem {
            image
        } else {
            image.with_container_name(name).with_network(NETWORK)
        };

        let node = docker.run(image);

        let host = if env_config.local_golem {
            "localhost"
        } else {
            name
        };

        let port = if env_config.local_golem {
            node.get_host_port_ipv4(5432)
        } else {
            5432
        };

        let local_port = node.get_host_port_ipv4(5432);

        let res = Db {
            inner: DbInner::Postgres {
                host: host.to_string(),
                port,
                local_port,
                _node: node,
            },
        };

        Db::test_started(local_port)?;

        Ok(res)
    }

    fn connection_string(local_port: u16) -> String {
        format!(
            "postgres://postgres:postgres@127.0.0.1:{}/postgres",
            local_port,
        )
    }

    pub fn info(&self) -> DbInfo {
        match &self.inner {
            DbInner::Sqlite(path) => DbInfo::Sqlite(path.clone()),
            DbInner::Postgres {
                host,
                port,
                local_port,
                _node: _,
            } => DbInfo::Postgres(PostgresInfo {
                host: host.clone(),
                port: port.clone(),
                local_port: local_port.clone(),
                database_name: "postgres".to_owned(),
                username: "postgres".to_owned(),
                password: "postgres".to_owned(),
            }),
        }
    }
}

#[derive(Debug)]
pub enum DbInfo {
    Sqlite(PathBuf),
    Postgres(PostgresInfo),
}

impl DbInfo {
    pub fn env(&self) -> HashMap<String, String> {
        match self {
            DbInfo::Postgres(pg) => pg.env(),
            DbInfo::Sqlite(db_path) => [
                ("GOLEM__DB__TYPE".to_string(), "Sqlite".to_string()),
                (
                    "GOLEM__DB__CONFIG__DATABASE".to_string(),
                    db_path
                        .to_str()
                        .expect("Invalid Sqlite database path")
                        .to_string(),
                ),
                (
                    "GOLEM__DB__CONFIG__MAX_CONNECTIONS".to_string(),
                    "10".to_string(),
                ),
            ]
            .into(),
        }
    }
}

#[derive(Debug)]
pub struct PostgresInfo {
    pub host: String,
    pub port: u16,
    pub local_port: u16,
    pub database_name: String,
    pub username: String,
    pub password: String,
}

impl PostgresInfo {
    pub fn connection_string(&self) -> String {
        format!(
            "postgres://{}:{}@{}:{}/{}",
            self.username, self.password, self.host, self.port, self.database_name
        )
    }

    pub fn env(&self) -> HashMap<String, String> {
        HashMap::from([
            ("DB_HOST".to_string(), self.host.clone()),
            ("DB_PORT".to_string(), self.port.to_string()),
            ("DB_NAME".to_string(), self.database_name.clone()),
            ("DB_USERNAME".to_string(), self.username.clone()),
            ("DB_PASSWORD".to_string(), self.password.clone()),
            ("COMPONENT_REPOSITORY_TYPE".to_string(), "jdbc".to_string()),
            ("GOLEM__DB__TYPE".to_string(), "Postgres".to_string()),
            (
                "GOLEM__DB__CONFIG__MAX_CONNECTIONS".to_string(),
                "10".to_string(),
            ),
            ("GOLEM__DB__CONFIG__HOST".to_string(), self.host.clone()),
            ("GOLEM__DB__CONFIG__PORT".to_string(), self.port.to_string()),
            (
                "GOLEM__DB__CONFIG__DATABASE".to_string(),
                self.database_name.clone(),
            ),
            (
                "GOLEM__DB__CONFIG__USERNAME".to_string(),
                self.username.clone(),
            ),
            (
                "GOLEM__DB__CONFIG__PASSWORD".to_string(),
                self.password.clone(),
            ),
        ])
    }
}