Skip to content

Commit dfdc08f

Browse files
committed
feat!: Add mandatory provison_parts argument to SecretOperatorVolumeSourceBuilder::new
1 parent 2ba637e commit dfdc08f

7 files changed

Lines changed: 100 additions & 20 deletions

File tree

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,29 @@ pub struct SecretOperatorVolumeSourceBuilder {
281281
kerberos_service_names: Vec<String>,
282282
tls_pkcs12_password: Option<String>,
283283
auto_tls_cert_lifetime: Option<Duration>,
284+
provision_parts: SecretOperatorVolumeProvisionParts,
284285
}
285286

286287
impl SecretOperatorVolumeSourceBuilder {
287-
pub fn new(secret_class: impl Into<String>) -> Self {
288+
/// Creates a builder for a secret-operator volume that uses the specified SecretClass to
289+
/// request the specified [`SecretOperatorVolumeProvisionParts`].
290+
///
291+
/// This function forces the caller to make an explicit choice if the public parts are
292+
/// sufficient or if private (e.g. a certificate for the Pod) parts are needed as well.
293+
/// This is done to avoid accidentally requesting too much parts. For details see
294+
/// [this issue](https://github.com/stackabletech/issues/issues/547).
295+
pub fn new(
296+
secret_class: impl Into<String>,
297+
provision_parts: SecretOperatorVolumeProvisionParts,
298+
) -> Self {
288299
Self {
289300
secret_class: secret_class.into(),
290301
scopes: Vec::new(),
291302
format: None,
292303
kerberos_service_names: Vec::new(),
293304
tls_pkcs12_password: None,
294305
auto_tls_cert_lifetime: None,
306+
provision_parts,
295307
}
296308
}
297309

@@ -342,6 +354,10 @@ impl SecretOperatorVolumeSourceBuilder {
342354

343355
annotations
344356
.insert(Annotation::secret_class(&self.secret_class).context(ParseAnnotationSnafu)?);
357+
annotations.insert(
358+
Annotation::secret_provision_parts(&self.provision_parts)
359+
.context(ParseAnnotationSnafu)?,
360+
);
345361

346362
if !self.scopes.is_empty() {
347363
annotations
@@ -417,6 +433,20 @@ pub enum SecretOperatorVolumeScope {
417433
ListenerVolume { name: String },
418434
}
419435

436+
/// What parts secret-operator should provision into the requested volume.
437+
#[derive(Clone, Debug, PartialEq, Eq, strum::AsRefStr)]
438+
#[strum(serialize_all = "kebab-case")]
439+
pub enum SecretOperatorVolumeProvisionParts {
440+
/// Only provision public parts, such as the CA certificate (either as PEM or truststore) or
441+
/// `krb5.conf`.
442+
Public,
443+
444+
/// Provision all parts, which includes all [`Public`](SecretOperatorVolumeProvisionParts::Public)
445+
/// ones as well as additional private parts, such as a TLS cert + private key, a keystore or a
446+
/// keytab.
447+
Full,
448+
}
449+
420450
/// Reference to a listener class or listener name
421451
#[derive(Clone, Debug, Eq, PartialEq)]
422452
pub enum ListenerReference {

crates/stackable-operator/src/commons/secret_class.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use serde::{Deserialize, Serialize};
44
use snafu::{ResultExt, Snafu};
55

66
use crate::builder::pod::volume::{
7-
SecretOperatorVolumeSourceBuilder, SecretOperatorVolumeSourceBuilderError, VolumeBuilder,
7+
SecretOperatorVolumeProvisionParts, SecretOperatorVolumeSourceBuilder,
8+
SecretOperatorVolumeSourceBuilderError, VolumeBuilder,
89
};
910

1011
#[derive(Debug, PartialEq, Snafu)]
@@ -38,9 +39,10 @@ impl SecretClassVolume {
3839

3940
pub fn to_ephemeral_volume_source(
4041
&self,
42+
provision_parts: SecretOperatorVolumeProvisionParts,
4143
) -> Result<EphemeralVolumeSource, SecretClassVolumeError> {
4244
let mut secret_operator_volume_builder =
43-
SecretOperatorVolumeSourceBuilder::new(&self.secret_class);
45+
SecretOperatorVolumeSourceBuilder::new(&self.secret_class, provision_parts);
4446

4547
if let Some(scope) = &self.scope {
4648
if scope.pod {
@@ -62,8 +64,12 @@ impl SecretClassVolume {
6264
.context(SecretOperatorVolumeSnafu)
6365
}
6466

65-
pub fn to_volume(&self, volume_name: &str) -> Result<Volume, SecretClassVolumeError> {
66-
let ephemeral = self.to_ephemeral_volume_source()?;
67+
pub fn to_volume(
68+
&self,
69+
volume_name: &str,
70+
provision_parts: SecretOperatorVolumeProvisionParts,
71+
) -> Result<Volume, SecretClassVolumeError> {
72+
let ephemeral = self.to_ephemeral_volume_source(provision_parts)?;
6773
Ok(VolumeBuilder::new(volume_name).ephemeral(ephemeral).build())
6874
}
6975
}
@@ -111,7 +117,8 @@ mod tests {
111117
listener_volumes: vec!["mylistener".to_string()],
112118
}),
113119
}
114-
.to_ephemeral_volume_source()
120+
// Let's assume we need some form of private data (e.g. a certificate or S3 credentials)
121+
.to_ephemeral_volume_source(SecretOperatorVolumeProvisionParts::Full)
115122
.unwrap();
116123

117124
let expected_volume_attributes = BTreeMap::from([
@@ -123,6 +130,10 @@ mod tests {
123130
"secrets.stackable.tech/scope".to_string(),
124131
"pod,service=myservice,listener-volume=mylistener".to_string(),
125132
),
133+
(
134+
"secrets.stackable.tech/provision-parts".to_string(),
135+
"full".to_string(),
136+
),
126137
]);
127138

128139
assert_eq!(

crates/stackable-operator/src/commons/tls_verification.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ use snafu::{ResultExt, Snafu};
66
use crate::{
77
builder::{
88
self,
9-
pod::{PodBuilder, container::ContainerBuilder, volume::VolumeMountBuilder},
9+
pod::{
10+
PodBuilder,
11+
container::ContainerBuilder,
12+
volume::{SecretOperatorVolumeProvisionParts, VolumeMountBuilder},
13+
},
1014
},
1115
commons::secret_class::{SecretClassVolume, SecretClassVolumeError},
1216
constants::secret::SECRET_BASE_PATH,
@@ -72,7 +76,8 @@ impl TlsClientDetails {
7276
let volume_name = format!("{secret_class}-ca-cert");
7377
let secret_class_volume = SecretClassVolume::new(secret_class.clone(), None);
7478
let volume = secret_class_volume
75-
.to_volume(&volume_name)
79+
// We only need the public CA cert
80+
.to_volume(&volume_name, SecretOperatorVolumeProvisionParts::Public)
7681
.context(SecretClassVolumeSnafu)?;
7782

7883
volumes.push(volume);

crates/stackable-operator/src/crd/authentication/ldap/v1alpha1_impl.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ use url::Url;
55
use crate::{
66
builder::{
77
self,
8-
pod::{PodBuilder, container::ContainerBuilder, volume::VolumeMountBuilder},
8+
pod::{
9+
PodBuilder,
10+
container::ContainerBuilder,
11+
volume::{SecretOperatorVolumeProvisionParts, VolumeMountBuilder},
12+
},
913
},
1014
commons::{secret_class::SecretClassVolumeError, tls_verification::TlsClientDetailsError},
1115
constants::secret::SECRET_BASE_PATH,
@@ -94,7 +98,8 @@ impl AuthenticationProvider {
9498
let secret_class = &bind_credentials.secret_class;
9599
let volume_name = format!("{secret_class}-bind-credentials");
96100
let volume = bind_credentials
97-
.to_volume(&volume_name)
101+
// We need the private LDAP bind credentials
102+
.to_volume(&volume_name, SecretOperatorVolumeProvisionParts::Full)
98103
.context(BindCredentialsSnafu)?;
99104

100105
volumes.push(volume);
@@ -234,7 +239,10 @@ mod tests {
234239
secret_class: "ldap-ca-cert".to_string(),
235240
scope: None,
236241
}
237-
.to_volume("ldap-ca-cert-ca-cert")
242+
.to_volume(
243+
"ldap-ca-cert-ca-cert",
244+
SecretOperatorVolumeProvisionParts::Public
245+
)
238246
.unwrap()
239247
]
240248
);
@@ -263,13 +271,19 @@ mod tests {
263271
secret_class: "openldap-bind-credentials".to_string(),
264272
scope: None,
265273
}
266-
.to_volume("openldap-bind-credentials-bind-credentials")
274+
.to_volume(
275+
"openldap-bind-credentials-bind-credentials",
276+
SecretOperatorVolumeProvisionParts::Full
277+
)
267278
.unwrap(),
268279
SecretClassVolume {
269280
secret_class: "ldap-ca-cert".to_string(),
270281
scope: None,
271282
}
272-
.to_volume("ldap-ca-cert-ca-cert")
283+
.to_volume(
284+
"ldap-ca-cert-ca-cert",
285+
SecretOperatorVolumeProvisionParts::Public
286+
)
273287
.unwrap()
274288
]
275289
);

crates/stackable-operator/src/crd/s3/connection/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,16 @@ mod tests {
174174
.unwrap()
175175
.annotations
176176
.unwrap(),
177-
&BTreeMap::from([(
178-
"secrets.stackable.tech/class".to_string(),
179-
"ionos-s3-credentials".to_string()
180-
)]),
177+
&BTreeMap::from([
178+
(
179+
"secrets.stackable.tech/class".to_string(),
180+
"ionos-s3-credentials".to_string()
181+
),
182+
(
183+
"secrets.stackable.tech/provision-parts".to_string(),
184+
"full".to_string()
185+
)
186+
]),
181187
);
182188

183189
assert_eq!(mount.name, volume.name);

crates/stackable-operator/src/crd/s3/connection/v1alpha1_impl.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ use snafu::{ResultExt as _, Snafu};
33
use url::Url;
44

55
use crate::{
6-
builder::pod::{PodBuilder, container::ContainerBuilder, volume::VolumeMountBuilder},
6+
builder::pod::{
7+
PodBuilder,
8+
container::ContainerBuilder,
9+
volume::{SecretOperatorVolumeProvisionParts, VolumeMountBuilder},
10+
},
711
client::Client,
812
commons::{secret_class::SecretClassVolumeError, tls_verification::TlsClientDetailsError},
913
constants::secret::SECRET_BASE_PATH,
@@ -110,7 +114,8 @@ impl ConnectionSpec {
110114

111115
volumes.push(
112116
credentials
113-
.to_volume(&volume_name)
117+
// We need the private S3 credentials
118+
.to_volume(&volume_name, SecretOperatorVolumeProvisionParts::Full)
114119
.context(AddS3CredentialVolumesSnafu)?,
115120
);
116121
mounts.push(

crates/stackable-operator/src/kvp/annotation/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::{
1818
use delegate::delegate;
1919

2020
use crate::{
21-
builder::pod::volume::SecretOperatorVolumeScope,
21+
builder::pod::volume::{SecretOperatorVolumeProvisionParts, SecretOperatorVolumeScope},
2222
iter::TryFromIterator,
2323
kvp::{Key, KeyValuePair, KeyValuePairError, KeyValuePairs, KeyValuePairsError},
2424
};
@@ -80,6 +80,15 @@ impl Annotation {
8080
self.0
8181
}
8282

83+
/// Constructs a `secrets.stackable.tech/provision-parts` annotation.
84+
pub fn secret_provision_parts(
85+
provision_parts: &SecretOperatorVolumeProvisionParts,
86+
) -> Result<Self, AnnotationError> {
87+
let kvp =
88+
KeyValuePair::try_from(("secrets.stackable.tech/provision-parts", provision_parts))?;
89+
Ok(Self(kvp))
90+
}
91+
8392
/// Constructs a `secrets.stackable.tech/class` annotation.
8493
pub fn secret_class(secret_class: &str) -> Result<Self, AnnotationError> {
8594
let kvp = KeyValuePair::try_from(("secrets.stackable.tech/class", secret_class))?;

0 commit comments

Comments
 (0)