Skip to content

Commit d207046

Browse files
committed
feat: Support putting TrustStore information in Secret
1 parent c26494b commit d207046

File tree

13 files changed

+130
-40
lines changed

13 files changed

+130
-40
lines changed

deploy/helm/secret-operator/crds/crds.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,18 @@ spec:
392392
- kerberos
393393
nullable: true
394394
type: string
395+
outputResource:
396+
default: ConfigMap
397+
description: |-
398+
Which Kubernetes resource should be used to output the requested information to.
399+
400+
The trust information (such as a `ca.crt`) can be considered public information, so we put it in a `ConfigMap` by default. However, some tools (such as OpenShift routes) require it to be placed in a `Secret`, so we also support that.
401+
402+
Can be either `ConfigMap` or `Secret`, defaults to `ConfigMap`.
403+
enum:
404+
- Secret
405+
- ConfigMap
406+
type: string
395407
secretClassName:
396408
description: The name of the SecretClass that the request concerns.
397409
type: string

rust/operator-binary/src/crd.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,10 +518,28 @@ pub struct TrustStoreSpec {
518518
/// The name of the SecretClass that the request concerns.
519519
pub secret_class_name: String,
520520

521+
/// Which Kubernetes resource should be used to output the requested information to.
522+
///
523+
/// The trust information (such as a `ca.crt`) can be considered public information, so we put
524+
/// it in a `ConfigMap` by default. However, some tools (such as OpenShift routes) require it
525+
/// to be placed in a `Secret`, so we also support that.
526+
///
527+
/// Can be either `ConfigMap` or `Secret`, defaults to `ConfigMap`.
528+
#[serde(default)]
529+
pub output_resource: TrustStoreOutputType,
530+
521531
/// The [format](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass#format) that the data should be converted into.
522532
pub format: Option<SecretFormat>,
523533
}
524534

535+
#[derive(Clone, Debug, Default, PartialEq, JsonSchema, Serialize, Deserialize)]
536+
pub enum TrustStoreOutputType {
537+
Secret,
538+
539+
#[default]
540+
ConfigMap,
541+
}
542+
525543
#[cfg(test)]
526544
mod test {
527545
use super::*;

rust/operator-binary/src/truststore_controller.rs

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use strum::{EnumDiscriminants, IntoStaticStr};
3232
use crate::{
3333
OPERATOR_NAME,
3434
backend::{self, SecretBackendError, TrustSelector},
35-
crd::{SearchNamespaceMatchCondition, SecretClass, TrustStore},
35+
crd::{SearchNamespaceMatchCondition, SecretClass, TrustStore, TrustStoreOutputType},
3636
format::{
3737
self,
3838
well_known::{CompatibilityOptions, NamingOptions},
@@ -82,6 +82,11 @@ pub async fn start(client: &stackable_operator::client::Client, watch_namespace:
8282
watch_namespace.get_api::<PartialObjectMeta<ConfigMap>>(client),
8383
watcher::Config::default(),
8484
)
85+
// TODO: merge this into the other Secret watch
86+
.owns(
87+
watch_namespace.get_api::<PartialObjectMeta<Secret>>(client),
88+
watcher::Config::default(),
89+
)
8590
.watches(
8691
watch_namespace.get_api::<PartialObjectMeta<ConfigMap>>(client),
8792
watcher::Config::default(),
@@ -205,7 +210,14 @@ pub enum Error {
205210
source: stackable_operator::client::Error,
206211
config_map: ObjectRef<ConfigMap>,
207212
},
213+
214+
#[snafu(display("failed to apply target {secret} for the TrustStore"))]
215+
ApplyTrustStoreSecret {
216+
source: stackable_operator::client::Error,
217+
secret: ObjectRef<Secret>,
218+
},
208219
}
220+
209221
type Result<T, E = Error> = std::result::Result<T, E>;
210222
impl ReconcilerError for Error {
211223
fn category(&self) -> &'static str {
@@ -222,6 +234,7 @@ impl ReconcilerError for Error {
222234
Error::FormatData { secret_class, .. } => Some(secret_class.clone().erase()),
223235
Error::BuildOwnerReference { .. } => None,
224236
Error::ApplyTrustStoreConfigMap { config_map, .. } => Some(config_map.clone().erase()),
237+
Error::ApplyTrustStoreSecret { secret, .. } => Some(secret.clone().erase()),
225238
}
226239
}
227240
}
@@ -264,7 +277,7 @@ async fn reconcile(
264277
.get_trust_data(&selector)
265278
.await
266279
.context(BackendGetTrustDataSnafu)?;
267-
let (Flattened(string_data), Flattened(binary_data)) = trust_data
280+
let trust_file_contents = trust_data
268281
.data
269282
.into_files(
270283
truststore.spec.format,
@@ -273,30 +286,53 @@ async fn reconcile(
273286
)
274287
.context(FormatDataSnafu {
275288
secret_class: secret_class_ref,
276-
})?
289+
})?;
290+
let (Flattened(string_data), Flattened(binary_data)) = trust_file_contents
277291
.into_iter()
278-
// Try to put valid UTF-8 data into `data`, but fall back to `binary_data` otherwise
292+
// Try to put valid UTF-8 data into `string_data`, but fall back to `binary_data` otherwise
279293
.map(|(k, v)| match String::from_utf8(v) {
280294
Ok(v) => (Some((k, v)), None),
281295
Err(v) => (None, Some((k, ByteString(v.into_bytes())))),
282296
})
283297
.collect();
284-
let trust_cm = ConfigMap {
285-
metadata: ObjectMetaBuilder::new()
286-
.name_and_namespace(truststore)
287-
.ownerreference_from_resource(truststore, None, Some(true))
288-
.context(BuildOwnerReferenceSnafu)?
289-
.build(),
290-
data: Some(string_data),
291-
binary_data: Some(binary_data),
292-
..Default::default()
293-
};
294-
ctx.client
295-
.apply_patch(CONTROLLER_NAME, &trust_cm, &trust_cm)
296-
.await
297-
.context(ApplyTrustStoreConfigMapSnafu {
298-
config_map: &trust_cm,
299-
})?;
298+
299+
let trust_metadata = ObjectMetaBuilder::new()
300+
.name_and_namespace(truststore)
301+
.ownerreference_from_resource(truststore, None, Some(true))
302+
.context(BuildOwnerReferenceSnafu)?
303+
.build();
304+
305+
match truststore.spec.output_resource {
306+
TrustStoreOutputType::ConfigMap => {
307+
let trust_cm = ConfigMap {
308+
metadata: trust_metadata,
309+
data: Some(string_data),
310+
binary_data: Some(binary_data),
311+
..Default::default()
312+
};
313+
ctx.client
314+
.apply_patch(CONTROLLER_NAME, &trust_cm, &trust_cm)
315+
.await
316+
.context(ApplyTrustStoreConfigMapSnafu {
317+
config_map: &trust_cm,
318+
})?;
319+
}
320+
TrustStoreOutputType::Secret => {
321+
let trust_secret = Secret {
322+
metadata: trust_metadata,
323+
string_data: Some(string_data),
324+
data: Some(binary_data),
325+
..Default::default()
326+
};
327+
ctx.client
328+
.apply_patch(CONTROLLER_NAME, &trust_secret, &trust_secret)
329+
.await
330+
.context(ApplyTrustStoreSecretSnafu {
331+
secret: &trust_secret,
332+
})?;
333+
}
334+
}
335+
300336
Ok(controller::Action::await_change())
301337
}
302338

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
commands:
5+
- script: envsubst '$NAMESPACE' < 01_secretclass.yaml | kubectl --namespace=$NAMESPACE apply -f -

tests/templates/kuttl/tls-truststore/01-secretclass.yaml

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/templates/kuttl/tls-truststore/02-assert.yaml renamed to tests/templates/kuttl/tls-truststore/02-assert.yaml.j2

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ kind: TestAssert
44
timeout: 5
55
---
66
apiVersion: v1
7-
kind: ConfigMap
7+
kind: {{ test_scenario['values']['truststore-output-resource'] }}
88
metadata:
99
name: truststore-pem
1010
# data is validated in 03-assert.yaml
1111
---
1212
apiVersion: v1
13-
kind: ConfigMap
13+
kind: {{ test_scenario['values']['truststore-output-resource'] }}
1414
metadata:
1515
name: truststore-pkcs12
1616
# data is validated in 03-assert.yaml
1717
---
18+
{% if test_scenario['values']['truststore-output-resource'] == 'ConfigMap' %}
1819
apiVersion: v1
1920
kind: ConfigMap
2021
metadata:
@@ -26,3 +27,13 @@ data:
2627
binaryData:
2728
# Should stay binary since it is not legal UTF-8
2829
actuallyBinary: aWxsZWdhbIB1dGYtOA==
30+
{% else %}
31+
apiVersion: v1
32+
kind: Secret
33+
metadata:
34+
name: truststore-k8ssearch
35+
data:
36+
foo: YmFy
37+
baz: aGVsbG8=
38+
actuallyBinary: aWxsZWdhbIB1dGYtOA==
39+
{% endif %}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
apiVersion: kuttl.dev/v1beta1
3+
kind: TestStep
4+
commands:
5+
- script: envsubst '$NAMESPACE' < 02_truststore.yaml | kubectl --namespace=$NAMESPACE apply -f -

tests/templates/kuttl/tls-truststore/02-truststore.yaml

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/templates/kuttl/tls-truststore/truststore.yaml renamed to tests/templates/kuttl/tls-truststore/02_truststore.yaml.j2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ metadata:
77
spec:
88
secretClassName: tls-$NAMESPACE
99
format: tls-pem
10+
outputResource: {{ test_scenario['values']['truststore-output-resource'] }}
1011
---
1112
apiVersion: secrets.stackable.tech/v1alpha1
1213
kind: TrustStore
@@ -15,10 +16,12 @@ metadata:
1516
spec:
1617
secretClassName: tls-$NAMESPACE
1718
format: tls-pkcs12
19+
outputResource: {{ test_scenario['values']['truststore-output-resource'] }}
1820
---
1921
apiVersion: secrets.stackable.tech/v1alpha1
2022
kind: TrustStore
2123
metadata:
2224
name: truststore-k8ssearch
2325
spec:
2426
secretClassName: k8ssearch-$NAMESPACE
27+
outputResource: {{ test_scenario['values']['truststore-output-resource'] }}

0 commit comments

Comments
 (0)