Skip to content

Commit 8abea6e

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/config-overrides-json
2 parents 8654602 + f3df803 commit 8abea6e

21 files changed

Lines changed: 1341 additions & 44 deletions

File tree

crates/stackable-operator/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- Add generic database connection mechanism ([#1163]).
10+
11+
### Changed
12+
13+
- BREAKING: Change signature of `ContainerBuilder::add_env_vars` from `Vec<EnvVar>` to `IntoIterator<Item = EnvVar>` ([#1163]).
14+
15+
[#1163]: https://github.com/stackabletech/operator-rs/pull/1163
16+
717
## [0.109.0] - 2026-04-07
818

919
### Added

crates/stackable-operator/crds/DummyCluster.yaml

Lines changed: 191 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -82,57 +82,206 @@ spec:
8282
and `stopped` will take no effect until `reconciliationPaused` is set to false or removed.
8383
type: boolean
8484
type: object
85-
configOverrides:
86-
default: {}
87-
description: |-
88-
Typed config override strategies for Dummy config files.
89-
90-
Demonstrates both JSON-formatted (`config.json`) and key-value-formatted
91-
(`dummy.properties`) config file overrides.
85+
databaseConnection:
86+
oneOf:
87+
- required:
88+
- postgresql
89+
- required:
90+
- mysql
91+
- required:
92+
- derby
93+
- required:
94+
- redis
95+
- required:
96+
- genericJdbc
97+
- required:
98+
- genericSqlAlchemy
99+
- required:
100+
- genericCelery
92101
properties:
93-
config.json:
94-
description: Overrides for the `config.json` file.
95-
nullable: true
96-
oneOf:
97-
- required:
98-
- jsonMergePatch
99-
- required:
100-
- jsonPatches
101-
- required:
102-
- userProvided
102+
derby:
103+
description: |-
104+
Connection settings for an embedded [Apache Derby](https://db.apache.org/derby/) database.
105+
106+
Derby is an embedded, file-based Java database engine that requires no separate server process.
107+
It is typically used for development, testing, or as a lightweight metastore backend (e.g. for
108+
Apache Hive).
103109
properties:
104-
jsonMergePatch:
105-
description: |-
106-
Can be set to arbitrary YAML content, which is converted to JSON and used as
107-
[RFC 7396](https://datatracker.ietf.org/doc/html/rfc7396) JSON merge patch.
108-
type: object
109-
x-kubernetes-preserve-unknown-fields: true
110-
jsonPatches:
110+
location:
111111
description: |-
112-
List of [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902) JSON patches.
113-
114-
Can be used when more flexibility is needed, e.g. to only modify elements
115-
in a list based on a condition.
112+
Path on the filesystem where Derby stores its database files.
116113
117-
A patch looks something like
114+
If not specified, defaults to `/tmp/derby/{unique_database_name}/derby.db`.
115+
The `{unique_database_name}` part is automatically handled by the operator and is added to
116+
prevent clashing database files. The `create=true` flag is always appended to the JDBC URL,
117+
so the database is created automatically if it does not yet exist at this location.
118+
nullable: true
119+
type: string
120+
type: object
121+
genericCelery:
122+
description: |-
123+
A generic Celery database connection for broker or result backend types not covered by a
124+
dedicated variant.
118125
119-
`{"op": "test", "path": "/0/name", "value": "Andrew"}`
126+
Use this when you need a Celery-compatible connection that does not have a first-class
127+
connection type. The complete connection URL is read from a Secret, giving the user full
128+
control over the connection string.
129+
properties:
130+
connectionUrlSecretName:
131+
description: The name of the Secret that contains an `connectionUrl` key with the complete Celery URL.
132+
type: string
133+
required:
134+
- connectionUrlSecretName
135+
type: object
136+
genericJdbc:
137+
description: |-
138+
A generic JDBC database connection for database types not covered by a dedicated variant.
120139
121-
or
140+
Use this when you need to connect to a JDBC-compatible database that does not have a
141+
first-class connection type. You are responsible for providing the correct driver class name
142+
and a fully-formed JDBC URL as well as providing the needed classes on the Java classpath.
143+
properties:
144+
credentialsSecretName:
145+
description: |-
146+
Name of a Secret containing the `username` and `password` keys used to authenticate
147+
against the database.
148+
type: string
149+
driver:
150+
description: |-
151+
Fully-qualified Java class name of the JDBC driver, e.g. `org.postgresql.Driver` or
152+
`com.mysql.jdbc.Driver`. The driver JAR must be provided by you on the classpath.
153+
type: string
154+
url:
155+
description: |-
156+
The JDBC connection URL, e.g. `jdbc:postgresql://my-host:5432/mydb`. Credentials must
157+
not be embedded in this URL; they are instead injected via environment variables sourced
158+
from `credentials_secret`.
159+
format: uri
160+
type: string
161+
required:
162+
- credentialsSecretName
163+
- driver
164+
- url
165+
type: object
166+
genericSqlAlchemy:
167+
description: |-
168+
A generic SQLAlchemy database connection for database types not covered by a dedicated variant.
122169
123-
`{"op": "add", "path": "/0/happy", "value": true}`
124-
items:
170+
Use this when you need to connect to a SQLAlchemy-compatible database that does not have a
171+
first-class connection type. The complete connection URL is read from a Secret, giving the user
172+
full control over the connection string including any driver-specific options.
173+
properties:
174+
connectionUrlSecretName:
175+
description: The name of the Secret that contains an `connectionUrl` key with the complete SQLAlchemy URL.
176+
type: string
177+
required:
178+
- connectionUrlSecretName
179+
type: object
180+
mysql:
181+
description: Connection settings for a [MySQL](https://www.mysql.com/) database.
182+
properties:
183+
credentialsSecretName:
184+
description: |-
185+
Name of a Secret containing the `username` and `password` keys used to authenticate
186+
against the MySQL server.
187+
type: string
188+
database:
189+
description: Name of the database (schema) to connect to.
190+
type: string
191+
host:
192+
description: Hostname or IP address of the MySQL server.
193+
type: string
194+
parameters:
195+
additionalProperties:
125196
type: string
126-
type: array
127-
userProvided:
128-
description: Override the entire config file with the specified String.
197+
default: {}
198+
description: |-
199+
Additional map of connection parameters to append to the connection URL. The given
200+
`HashMap<String, String>` will be converted to query parameters in the form of
201+
`?param1=value1&param2=value2`.
202+
type: object
203+
port:
204+
default: 3306
205+
description: Port the MySQL server is listening on. Defaults to `3306`.
206+
format: uint16
207+
maximum: 65535.0
208+
minimum: 0.0
209+
type: integer
210+
required:
211+
- credentialsSecretName
212+
- database
213+
- host
214+
type: object
215+
postgresql:
216+
description: Connection settings for a [PostgreSQL](https://www.postgresql.org/) database.
217+
properties:
218+
credentialsSecretName:
219+
description: |-
220+
Name of a Secret containing the `username` and `password` keys used to authenticate
221+
against the PostgreSQL server.
222+
type: string
223+
database:
224+
description: Name of the database (schema) to connect to.
129225
type: string
226+
host:
227+
description: Hostname or IP address of the PostgreSQL server.
228+
type: string
229+
parameters:
230+
additionalProperties:
231+
type: string
232+
default: {}
233+
description: |-
234+
Additional map of JDBC connection parameters to append to the connection URL. The given
235+
`HashMap<String, String>` will be converted to query parameters in the form of
236+
`?param1=value1&param2=value2`.
237+
type: object
238+
port:
239+
default: 5432
240+
description: Port the PostgreSQL server is listening on. Defaults to `5432`.
241+
format: uint16
242+
maximum: 65535.0
243+
minimum: 0.0
244+
type: integer
245+
required:
246+
- credentialsSecretName
247+
- database
248+
- host
130249
type: object
131-
dummy.properties:
132-
additionalProperties:
133-
type: string
134-
description: Overrides for the `dummy.properties` file.
135-
nullable: true
250+
redis:
251+
description: |-
252+
Connection settings for a [Redis](https://redis.io/) instance.
253+
254+
Redis is commonly used as a Celery message broker or result backend (e.g. for Apache Airflow).
255+
properties:
256+
credentialsSecretName:
257+
description: |-
258+
Name of a Secret containing the `username` and `password` keys used to authenticate
259+
against the Redis server.
260+
type: string
261+
databaseId:
262+
default: 0
263+
description: |-
264+
Numeric index of the Redis logical database to use. Defaults to `0`.
265+
266+
Redis supports multiple logical databases within a single instance, identified by an
267+
integer index. Database `0` is the default.
268+
format: uint16
269+
maximum: 65535.0
270+
minimum: 0.0
271+
type: integer
272+
host:
273+
description: Hostname or IP address of the Redis server.
274+
type: string
275+
port:
276+
default: 6379
277+
description: Port the Redis server is listening on. Defaults to `6379`.
278+
format: uint16
279+
maximum: 65535.0
280+
minimum: 0.0
281+
type: integer
282+
required:
283+
- credentialsSecretName
284+
- host
136285
type: object
137286
type: object
138287
domainName:
@@ -2321,6 +2470,7 @@ spec:
23212470
required:
23222471
- clientAuthenticationDetails
23232472
- clusterOperation
2473+
- databaseConnection
23242474
- domainName
23252475
- gitSync
23262476
- hostName

crates/stackable-operator/src/builder/pod/container.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ impl ContainerBuilder {
175175
self
176176
}
177177

178-
pub fn add_env_vars(&mut self, env_vars: Vec<EnvVar>) -> &mut Self {
178+
pub fn add_env_vars<I>(&mut self, env_vars: I) -> &mut Self
179+
where
180+
I: IntoIterator<Item = EnvVar>,
181+
{
179182
self.env.get_or_insert_with(Vec::new).extend(env_vars);
180183
self
181184
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use k8s_openapi::api::core::v1::{EnvVar, EnvVarSource, SecretKeySelector};
2+
3+
pub fn env_var_with_value_from_secret(
4+
env_var_name: impl Into<String>,
5+
secret_name: impl Into<String>,
6+
secret_key: impl Into<String>,
7+
) -> EnvVar {
8+
EnvVar {
9+
name: env_var_name.into(),
10+
value_from: Some(EnvVarSource {
11+
secret_key_ref: Some(SecretKeySelector {
12+
name: secret_name.into(),
13+
key: secret_key.into(),
14+
..Default::default()
15+
}),
16+
..Default::default()
17+
}),
18+
..Default::default()
19+
}
20+
}

crates/stackable-operator/src/builder/pod/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::{
2929
};
3030

3131
pub mod container;
32+
pub mod env;
3233
pub mod probe;
3334
pub mod resources;
3435
pub mod security;

crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ impl GitSyncResources {
197197
one_time,
198198
container_log_config,
199199
)])
200-
.add_env_vars(env_vars.into())
200+
.add_env_vars(env_vars.iter().cloned())
201201
.add_volume_mounts(volume_mounts.to_vec())
202202
.context(AddVolumeMountSnafu)?
203203
.resources(

crates/stackable-operator/src/crd/git_sync/v1alpha2_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ impl GitSyncResources {
314314
container_log_config,
315315
ca_cert_path,
316316
)])
317-
.add_env_vars(env_vars.into())
317+
.add_env_vars(env_vars.iter().cloned())
318318
.add_volume_mounts(volume_mounts.to_vec())
319319
.context(AddVolumeMountSnafu)?
320320
.resources(

0 commit comments

Comments
 (0)