Skip to content

Commit cfa085d

Browse files
committed
Support specifying the templating mechanism
1 parent 33ae519 commit cfa085d

9 files changed

Lines changed: 130 additions & 37 deletions

File tree

crates/stackable-operator/src/databases/databases/derby.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use schemars::JsonSchema;
22
use serde::{Deserialize, Serialize};
33
use snafu::{ResultExt, Snafu};
44

5-
use crate::databases::drivers::jdbc::{JDBCDatabaseConnection, JDBCDatabaseConnectionDetails};
5+
use crate::databases::{
6+
TemplatingMechanism,
7+
drivers::jdbc::{JDBCDatabaseConnection, JDBCDatabaseConnectionDetails},
8+
};
69

710
#[derive(Debug, Snafu)]
811
pub enum Error {
@@ -19,9 +22,10 @@ pub struct DerbyConnection {
1922
}
2023

2124
impl JDBCDatabaseConnection for DerbyConnection {
22-
fn jdbc_connection_details(
25+
fn jdbc_connection_details_with_templating(
2326
&self,
2427
unique_database_name: &str,
28+
_templating_mechanism: &TemplatingMechanism,
2529
) -> Result<JDBCDatabaseConnectionDetails, crate::databases::Error> {
2630
let location = self
2731
.location

crates/stackable-operator/src/databases/databases/mysql.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use snafu::{ResultExt, Snafu};
77
use crate::{
88
commons::networking::HostName,
99
databases::{
10+
TemplatingMechanism,
1011
drivers::jdbc::{JDBCDatabaseConnection, JDBCDatabaseConnectionDetails},
1112
helpers::{connection_parameters_as_url_query_parameters, username_and_password_envs},
1213
},
@@ -47,9 +48,10 @@ impl MysqlConnection {
4748
}
4849

4950
impl JDBCDatabaseConnection for MysqlConnection {
50-
fn jdbc_connection_details(
51+
fn jdbc_connection_details_with_templating(
5152
&self,
5253
unique_database_name: &str,
54+
_templating_mechanism: &TemplatingMechanism,
5355
) -> Result<JDBCDatabaseConnectionDetails, crate::databases::Error> {
5456
let Self {
5557
host,

crates/stackable-operator/src/databases/databases/postgresql.rs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use snafu::{ResultExt, Snafu};
77
use crate::{
88
commons::networking::HostName,
99
databases::{
10+
TemplatingMechanism,
1011
drivers::{
1112
celery::{CeleryDatabaseConnection, CeleryDatabaseConnectionDetails},
1213
jdbc::{JDBCDatabaseConnection, JDBCDatabaseConnectionDetails},
@@ -51,9 +52,10 @@ impl PostgresqlConnection {
5152
}
5253

5354
impl JDBCDatabaseConnection for PostgresqlConnection {
54-
fn jdbc_connection_details(
55+
fn jdbc_connection_details_with_templating(
5556
&self,
5657
unique_database_name: &str,
58+
_templating_mechanism: &TemplatingMechanism,
5759
) -> Result<JDBCDatabaseConnectionDetails, crate::databases::Error> {
5860
let Self {
5961
host,
@@ -81,9 +83,10 @@ impl JDBCDatabaseConnection for PostgresqlConnection {
8183
}
8284

8385
impl SQLAlchemyDatabaseConnection for PostgresqlConnection {
84-
fn sqlalchemy_connection_details(
86+
fn sqlalchemy_connection_details_with_templating(
8587
&self,
8688
unique_database_name: &str,
89+
templating_mechanism: &TemplatingMechanism,
8790
) -> SQLAlchemyDatabaseConnectionDetails {
8891
let Self {
8992
host,
@@ -94,13 +97,18 @@ impl SQLAlchemyDatabaseConnection for PostgresqlConnection {
9497
} = self;
9598
let (username_env, password_env) =
9699
username_and_password_envs(unique_database_name, credentials_secret);
97-
98-
let uri_template = format!(
99-
"postgresql+psycopg2://${{env:{username_env_name}}}:${{env:{password_env_name}}}@{host}:{port}/{database}{parameters}",
100-
username_env_name = username_env.name,
101-
password_env_name = password_env.name,
102-
parameters = connection_parameters_as_url_query_parameters(parameters)
103-
);
100+
let username_env_name = &username_env.name;
101+
let password_env_name = &password_env.name;
102+
let parameters = connection_parameters_as_url_query_parameters(parameters);
103+
104+
let uri_template = match templating_mechanism {
105+
TemplatingMechanism::ConfigUtils => format!(
106+
"postgresql+psycopg2://${{env:{username_env_name}}}:${{env:{password_env_name}}}@{host}:{port}/{database}{parameters}",
107+
),
108+
TemplatingMechanism::BashEnvSubstitution => format!(
109+
"postgresql+psycopg2://${{{username_env_name}}}:${{{password_env_name}}}@{host}:{port}/{database}{parameters}",
110+
),
111+
};
104112
SQLAlchemyDatabaseConnectionDetails {
105113
uri_template,
106114
username_env: Some(username_env),
@@ -111,9 +119,10 @@ impl SQLAlchemyDatabaseConnection for PostgresqlConnection {
111119
}
112120

113121
impl CeleryDatabaseConnection for PostgresqlConnection {
114-
fn celery_connection_details(
122+
fn celery_connection_details_with_templating(
115123
&self,
116124
unique_database_name: &str,
125+
templating_mechanism: &TemplatingMechanism,
117126
) -> CeleryDatabaseConnectionDetails {
118127
let Self {
119128
host,
@@ -124,13 +133,18 @@ impl CeleryDatabaseConnection for PostgresqlConnection {
124133
} = self;
125134
let (username_env, password_env) =
126135
username_and_password_envs(unique_database_name, credentials_secret);
127-
128-
let uri_template = format!(
129-
"db+postgresql://${{env:{username_env_name}}}:${{env:{password_env_name}}}@{host}:{port}/{database}{parameters}",
130-
username_env_name = username_env.name,
131-
password_env_name = password_env.name,
132-
parameters = connection_parameters_as_url_query_parameters(parameters)
133-
);
136+
let username_env_name = &username_env.name;
137+
let password_env_name = &password_env.name;
138+
let parameters = connection_parameters_as_url_query_parameters(parameters);
139+
140+
let uri_template = match templating_mechanism {
141+
TemplatingMechanism::ConfigUtils => format!(
142+
"db+postgresql://${{env:{username_env_name}}}:${{env:{password_env_name}}}@{host}:{port}/{database}{parameters}",
143+
),
144+
TemplatingMechanism::BashEnvSubstitution => format!(
145+
"db+postgresql://${{{username_env_name}}}:${{{password_env_name}}}@{host}:{port}/{database}{parameters}",
146+
),
147+
};
134148
CeleryDatabaseConnectionDetails {
135149
uri_template,
136150
username_env: Some(username_env),

crates/stackable-operator/src/databases/databases/redis.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
44
use crate::{
55
commons::networking::HostName,
66
databases::{
7+
TemplatingMechanism,
78
drivers::celery::{CeleryDatabaseConnection, CeleryDatabaseConnectionDetails},
89
helpers::username_and_password_envs,
910
},
@@ -39,9 +40,10 @@ impl RedisConnection {
3940
}
4041

4142
impl CeleryDatabaseConnection for RedisConnection {
42-
fn celery_connection_details(
43+
fn celery_connection_details_with_templating(
4344
&self,
4445
unique_database_name: &str,
46+
templating_mechanism: &TemplatingMechanism,
4547
) -> CeleryDatabaseConnectionDetails {
4648
let Self {
4749
host,
@@ -51,12 +53,17 @@ impl CeleryDatabaseConnection for RedisConnection {
5153
} = self;
5254
let (username_env, password_env) =
5355
username_and_password_envs(unique_database_name, credentials_secret);
56+
let username_env_name = &username_env.name;
57+
let password_env_name = &password_env.name;
5458

55-
let uri_template = format!(
56-
"redis://${{env:{username_env_name}}}:${{{password_env_name}}}@{host}:{port}/{database_id}",
57-
username_env_name = username_env.name,
58-
password_env_name = password_env.name,
59-
);
59+
let uri_template = match templating_mechanism {
60+
TemplatingMechanism::ConfigUtils => format!(
61+
"redis://${{env:{username_env_name}}}:${{env:{password_env_name}}}@{host}:{port}/{database_id}",
62+
),
63+
TemplatingMechanism::BashEnvSubstitution => format!(
64+
"redis://${{{username_env_name}}}:${{{password_env_name}}}@{host}:{port}/{database_id}",
65+
),
66+
};
6067
CeleryDatabaseConnectionDetails {
6168
uri_template,
6269
username_env: Some(username_env),
@@ -87,7 +94,7 @@ mod tests {
8794
redis_connection.celery_connection_details(UNIQUE_DATABASE_NAME);
8895
assert_eq!(
8996
celery_connection_details.uri_template,
90-
"redis://${env:WORKER_QUEUE_DATABASE_USERNAME}:${WORKER_QUEUE_DATABASE_PASSWORD}@my-redis:42/13"
97+
"redis://${env:WORKER_QUEUE_DATABASE_USERNAME}:${env:WORKER_QUEUE_DATABASE_PASSWORD}@my-redis:42/13"
9198
);
9299
assert!(celery_connection_details.username_env.is_some());
93100
assert!(celery_connection_details.password_env.is_some());

crates/stackable-operator/src/databases/drivers/celery.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,28 @@ use k8s_openapi::api::core::v1::EnvVar;
22
use schemars::JsonSchema;
33
use serde::{Deserialize, Serialize};
44

5-
use crate::builder::pod::{container::ContainerBuilder, env::env_var_from_secret};
5+
use crate::{
6+
builder::pod::{container::ContainerBuilder, env::env_var_from_secret},
7+
databases::TemplatingMechanism,
8+
};
69

710
/// TODO docs
811
pub trait CeleryDatabaseConnection {
912
/// TODO docs, e.g. on what are valid characters for unique_database_name
1013
fn celery_connection_details(
1114
&self,
1215
unique_database_name: &str,
16+
) -> CeleryDatabaseConnectionDetails {
17+
self.celery_connection_details_with_templating(
18+
unique_database_name,
19+
&TemplatingMechanism::default(),
20+
)
21+
}
22+
23+
fn celery_connection_details_with_templating(
24+
&self,
25+
unique_database_name: &str,
26+
templating_mechanism: &TemplatingMechanism,
1327
) -> CeleryDatabaseConnectionDetails;
1428
}
1529

@@ -55,18 +69,23 @@ pub struct GenericCeleryDatabaseConnection {
5569
}
5670

5771
impl CeleryDatabaseConnection for GenericCeleryDatabaseConnection {
58-
fn celery_connection_details(
72+
fn celery_connection_details_with_templating(
5973
&self,
6074
unique_database_name: &str,
75+
templating_mechanism: &TemplatingMechanism,
6176
) -> CeleryDatabaseConnectionDetails {
6277
let uri_env_name = format!(
6378
"{upper}_DATABASE_URI",
6479
upper = unique_database_name.to_uppercase()
6580
);
6681
let uri_env_var = env_var_from_secret(&uri_env_name, &self.uri_secret, "uri");
82+
let uri_template = match templating_mechanism {
83+
TemplatingMechanism::ConfigUtils => format!("${{env:{uri_env_name}}}"),
84+
TemplatingMechanism::BashEnvSubstitution => format!("${{{uri_env_name}}}"),
85+
};
6786

6887
CeleryDatabaseConnectionDetails {
69-
uri_template: format!("${{env:{uri_env_name}}}"),
88+
uri_template,
7089
username_env: None,
7190
password_env: None,
7291
generic_uri_var: Some(uri_env_var),

crates/stackable-operator/src/databases/drivers/jdbc.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use serde::{Deserialize, Serialize};
44
use url::Url;
55

66
use crate::{
7-
builder::pod::container::ContainerBuilder, databases::helpers::username_and_password_envs,
7+
builder::pod::container::ContainerBuilder,
8+
databases::{TemplatingMechanism, helpers::username_and_password_envs},
89
};
910

1011
/// TODO docs
@@ -13,6 +14,17 @@ pub trait JDBCDatabaseConnection {
1314
fn jdbc_connection_details(
1415
&self,
1516
unique_database_name: &str,
17+
) -> Result<JDBCDatabaseConnectionDetails, crate::databases::Error> {
18+
self.jdbc_connection_details_with_templating(
19+
unique_database_name,
20+
&TemplatingMechanism::default(),
21+
)
22+
}
23+
24+
fn jdbc_connection_details_with_templating(
25+
&self,
26+
unique_database_name: &str,
27+
templating_mechanism: &TemplatingMechanism,
1628
) -> Result<JDBCDatabaseConnectionDetails, crate::databases::Error>;
1729
}
1830

@@ -53,9 +65,10 @@ pub struct GenericJDBCDatabaseConnection {
5365
}
5466

5567
impl JDBCDatabaseConnection for GenericJDBCDatabaseConnection {
56-
fn jdbc_connection_details(
68+
fn jdbc_connection_details_with_templating(
5769
&self,
5870
unique_database_name: &str,
71+
_templating_mechanism: &TemplatingMechanism,
5972
) -> Result<JDBCDatabaseConnectionDetails, crate::databases::Error> {
6073
let (username_env, password_env) =
6174
username_and_password_envs(unique_database_name, &self.credentials_secret);

crates/stackable-operator/src/databases/drivers/sqlalchemy.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,28 @@ use k8s_openapi::api::core::v1::EnvVar;
22
use schemars::JsonSchema;
33
use serde::{Deserialize, Serialize};
44

5-
use crate::builder::pod::{container::ContainerBuilder, env::env_var_from_secret};
5+
use crate::{
6+
builder::pod::{container::ContainerBuilder, env::env_var_from_secret},
7+
databases::TemplatingMechanism,
8+
};
69

710
/// TODO docs
811
pub trait SQLAlchemyDatabaseConnection {
912
/// TODO docs, e.g. on what are valid characters for unique_database_name
1013
fn sqlalchemy_connection_details(
1114
&self,
1215
unique_database_name: &str,
16+
) -> SQLAlchemyDatabaseConnectionDetails {
17+
self.sqlalchemy_connection_details_with_templating(
18+
unique_database_name,
19+
&TemplatingMechanism::default(),
20+
)
21+
}
22+
23+
fn sqlalchemy_connection_details_with_templating(
24+
&self,
25+
unique_database_name: &str,
26+
templating_mechanism: &TemplatingMechanism,
1327
) -> SQLAlchemyDatabaseConnectionDetails;
1428
}
1529

@@ -55,18 +69,23 @@ pub struct GenericSQLAlchemyDatabaseConnection {
5569
}
5670

5771
impl SQLAlchemyDatabaseConnection for GenericSQLAlchemyDatabaseConnection {
58-
fn sqlalchemy_connection_details(
72+
fn sqlalchemy_connection_details_with_templating(
5973
&self,
6074
unique_database_name: &str,
75+
templating_mechanism: &TemplatingMechanism,
6176
) -> SQLAlchemyDatabaseConnectionDetails {
6277
let uri_env_name = format!(
6378
"{upper}_DATABASE_URI",
6479
upper = unique_database_name.to_uppercase()
6580
);
6681
let uri_env_var = env_var_from_secret(&uri_env_name, &self.uri_secret, "uri");
82+
let uri_template = match templating_mechanism {
83+
TemplatingMechanism::ConfigUtils => format!("${{env:{uri_env_name}}}"),
84+
TemplatingMechanism::BashEnvSubstitution => format!("${{{uri_env_name}}}"),
85+
};
6786

6887
SQLAlchemyDatabaseConnectionDetails {
69-
uri_template: format!("${{env:{uri_env_name}}}"),
88+
uri_template,
7089
username_env: None,
7190
password_env: None,
7291
generic_uri_var: Some(uri_env_var),

crates/stackable-operator/src/databases/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,14 @@ pub enum Error {
2020
#[snafu(context(false), display("Derby error"))]
2121
Derby { source: databases::derby::Error },
2222
}
23+
24+
#[derive(Copy, Clone, Debug, Default)]
25+
pub enum TemplatingMechanism {
26+
/// Template files using <https://github.com/stackabletech/config-utils>, e.g.
27+
/// `${env:EXAMPLE_USERNAME}`
28+
#[default]
29+
ConfigUtils,
30+
31+
/// Let `bash` substitute the env variable, e.g. `${EXAMPLE_USERNAME}`.
32+
BashEnvSubstitution,
33+
}

crates/stackable-operator/src/databases/tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
66
use crate::{
77
builder::pod::container::ContainerBuilder,
88
databases::{
9+
TemplatingMechanism,
910
databases::{postgresql::PostgresqlConnection, redis::RedisConnection},
1011
drivers::{
1112
celery::{CeleryDatabaseConnection, GenericCeleryDatabaseConnection},
@@ -97,14 +98,17 @@ fn test_dummy_celery_database_usage() {
9798
// Apply actual config
9899
let celery_connection_details = dummy_celery_connection
99100
.as_celery_database_connection()
100-
.celery_connection_details("worker_queue");
101+
.celery_connection_details_with_templating(
102+
"worker_queue",
103+
&TemplatingMechanism::BashEnvSubstitution,
104+
);
101105
let mut container_builder = ContainerBuilder::new("my-container").unwrap();
102106
celery_connection_details.add_to_container(&mut container_builder);
103107
let container = container_builder.build();
104108

105109
assert_eq!(
106110
celery_connection_details.uri_template,
107-
"${env:WORKER_QUEUE_DATABASE_URI}"
111+
"${WORKER_QUEUE_DATABASE_URI}"
108112
);
109113
assert_eq!(
110114
container

0 commit comments

Comments
 (0)