Skip to content

Commit 5b25c0d

Browse files
committed
Make fn fallible
1 parent 6288d21 commit 5b25c0d

File tree

8 files changed

+129
-52
lines changed

8 files changed

+129
-52
lines changed

crates/stackable-operator/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file.
66

77
### Fixed
88

9-
- Avoid clashing volumes and mounts by only adding volumes or mounts if they do not already exist ([#871]).
9+
- BREAKING: Avoid clashing volumes and mounts by only adding volumes or mounts if they do not already exist. This makes functions such as `PodBuilder::add_volume` or `ContainerBuilder::add_volume_mount` as well as related ones fallible ([#871]).
1010

1111
### Changed
1212

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

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use k8s_openapi::api::core::v1::{
66
LifecycleHandler, ObjectFieldSelector, Probe, ResourceRequirements, SecretKeySelector,
77
SecurityContext, VolumeMount,
88
};
9-
use snafu::{ResultExt as _, Snafu};
9+
use snafu::{ensure, ResultExt as _, Snafu};
1010

1111
use crate::{
1212
commons::product_image_selection::ResolvedProductImage,
@@ -22,6 +22,15 @@ pub enum Error {
2222
source: validation::Errors,
2323
container_name: String,
2424
},
25+
26+
#[snafu(display(
27+
"The volumeMount is clashing with an already existing volumeMount with the same mountPath but a different content. \
28+
The existing volumeMount is {existing_volume_mount:?}, the new one is {new_volume_mount:?}"
29+
))]
30+
ClashingVolumeMountMountPath {
31+
existing_volume_mount: VolumeMount,
32+
new_volume_mount: VolumeMount,
33+
},
2534
}
2635

2736
/// A builder to build [`Container`] objects.
@@ -206,30 +215,29 @@ impl ContainerBuilder {
206215
///
207216
/// We could have made this function fallible, but decided not to do so for now, as it would be a bigger breaking
208217
/// change.
209-
pub fn add_volume_mount_struct(&mut self, volume_mount: VolumeMount) -> &mut Self {
218+
pub fn add_volume_mount_struct(&mut self, volume_mount: VolumeMount) -> Result<&mut Self> {
210219
if let Some(existing_volume_mount) = self.volume_mounts.get(&volume_mount.mount_path) {
211-
if !existing_volume_mount.eq(&volume_mount) {
212-
tracing::error!(
213-
?existing_volume_mount,
214-
new_volume_mount = ?volume_mount,
215-
"The operator tried to add a volumeMount to Container, which was already added with a different content! \
216-
The new volumeMount will be ignored."
217-
);
218-
}
220+
ensure!(
221+
existing_volume_mount.eq(&volume_mount),
222+
ClashingVolumeMountMountPathSnafu {
223+
existing_volume_mount: existing_volume_mount.clone(),
224+
new_volume_mount: volume_mount,
225+
}
226+
);
219227
} else {
220228
self.volume_mounts
221229
.insert(volume_mount.mount_path.clone(), volume_mount);
222230
}
223231

224-
self
232+
Ok(self)
225233
}
226234

227235
/// See [`Self::add_volume_mount_struct`] for details
228236
pub fn add_volume_mount(
229237
&mut self,
230238
name: impl Into<String>,
231239
path: impl Into<String>,
232-
) -> &mut Self {
240+
) -> Result<&mut Self> {
233241
self.add_volume_mount_struct(VolumeMount {
234242
name: name.into(),
235243
mount_path: path.into(),
@@ -241,11 +249,12 @@ impl ContainerBuilder {
241249
pub fn add_volume_mounts(
242250
&mut self,
243251
volume_mounts: impl IntoIterator<Item = VolumeMount>,
244-
) -> &mut Self {
252+
) -> Result<&mut Self> {
245253
for volume_mount in volume_mounts {
246-
self.add_volume_mount_struct(volume_mount);
254+
self.add_volume_mount_struct(volume_mount)?;
247255
}
248-
self
256+
257+
Ok(self)
249258
}
250259

251260
pub fn readiness_probe(&mut self, probe: Probe) -> &mut Self {
@@ -427,6 +436,7 @@ mod tests {
427436
.add_env_var_from_config_map("envFromConfigMap", "my-configmap", "my-key")
428437
.add_env_var_from_secret("envFromSecret", "my-secret", "my-key")
429438
.add_volume_mount("configmap", "/mount")
439+
.unwrap()
430440
.add_container_port(container_port_name, container_port)
431441
.resources(resources.clone())
432442
.add_container_ports(vec![ContainerPortBuilder::new(container_port_1)
@@ -543,6 +553,7 @@ mod tests {
543553
"input is 64 bytes long but must be no more than 63"
544554
)
545555
}
556+
_ => {}
546557
}
547558
// One characters shorter name is valid
548559
let max_len_container_name: String = long_container_name.chars().skip(1).collect();
@@ -616,6 +627,7 @@ mod tests {
616627
} => {
617628
assert!(dbg!(source.to_string()).contains(dbg!(expected_err_contains)));
618629
}
630+
_ => {}
619631
}
620632
}
621633
}

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

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use k8s_openapi::{
99
},
1010
apimachinery::pkg::{api::resource::Quantity, apis::meta::v1::ObjectMeta},
1111
};
12-
use snafu::{OptionExt, ResultExt, Snafu};
12+
use snafu::{ensure, OptionExt, ResultExt, Snafu};
1313
use tracing::warn;
1414

1515
use crate::kvp::Labels;
@@ -51,6 +51,14 @@ pub enum Error {
5151

5252
#[snafu(display("object is missing key {key:?}"))]
5353
MissingObjectKey { key: &'static str },
54+
55+
#[snafu(display("The volume {volume_name:?} is clashing with an already existing volume with the same name but a different content. \
56+
The existing volume is {existing_volume:?}, the new one is {new_volume:?}"))]
57+
ClashingVolumeName {
58+
volume_name: String,
59+
existing_volume: Volume,
60+
new_volume: Volume,
61+
},
5462
}
5563

5664
/// A builder to build [`Pod`] or [`PodTemplateSpec`] objects.
@@ -267,7 +275,7 @@ impl PodBuilder {
267275
&mut self,
268276
name: impl Into<String>,
269277
quantity: Option<Quantity>,
270-
) -> &mut Self {
278+
) -> Result<&mut Self> {
271279
self.add_volume(
272280
VolumeBuilder::new(name)
273281
.with_empty_dir(None::<String>, quantity)
@@ -285,30 +293,30 @@ impl PodBuilder {
285293
///
286294
/// We could have made this function fallible, but decided not to do so for now, as it would be a bigger breaking
287295
/// change.
288-
pub fn add_volume(&mut self, volume: Volume) -> &mut Self {
296+
pub fn add_volume(&mut self, volume: Volume) -> Result<&mut Self> {
289297
if let Some(existing_volume) = self.volumes.get(&volume.name) {
290-
if !existing_volume.eq(&volume) {
291-
tracing::error!(
292-
?existing_volume,
293-
new_volume = ?volume,
294-
"The operator tried to add a volume to Pod, which was already added with a different content! \
295-
The new volume will be ignored."
296-
);
297-
}
298+
ensure!(
299+
existing_volume.eq(&volume),
300+
ClashingVolumeNameSnafu {
301+
volume_name: volume.name.clone(),
302+
existing_volume: existing_volume.clone(),
303+
new_volume: volume
304+
}
305+
);
298306
} else {
299307
self.volumes.insert(volume.name.clone(), volume);
300308
}
301309

302-
self
310+
Ok(self)
303311
}
304312

305313
/// See [`Self::add_volume`] for details
306-
pub fn add_volumes(&mut self, volumes: Vec<Volume>) -> &mut Self {
314+
pub fn add_volumes(&mut self, volumes: Vec<Volume>) -> Result<&mut Self> {
307315
for volume in volumes {
308-
self.add_volume(volume);
316+
self.add_volume(volume)?;
309317
}
310318

311-
self
319+
Ok(self)
312320
}
313321

314322
/// Add a [`Volume`] for the storage class `listeners.stackable.tech` with the given listener
@@ -337,6 +345,7 @@ impl PodBuilder {
337345
/// ContainerBuilder::new("container")
338346
/// .unwrap()
339347
/// .add_volume_mount("listener", "/path/to/volume")
348+
/// .unwrap()
340349
/// .build(),
341350
/// )
342351
/// .add_listener_volume_by_listener_class("listener", "nodeport", &labels)
@@ -392,7 +401,7 @@ impl PodBuilder {
392401
name: volume_name.into(),
393402
ephemeral: Some(volume),
394403
..Volume::default()
395-
});
404+
})?;
396405

397406
Ok(self)
398407
}
@@ -423,6 +432,7 @@ impl PodBuilder {
423432
/// ContainerBuilder::new("container")
424433
/// .unwrap()
425434
/// .add_volume_mount("listener", "/path/to/volume")
435+
/// .unwrap()
426436
/// .build(),
427437
/// )
428438
/// .add_listener_volume_by_listener_name("listener", "preprovisioned-listener", &labels)
@@ -478,7 +488,7 @@ impl PodBuilder {
478488
name: volume_name.into(),
479489
ephemeral: Some(volume),
480490
..Volume::default()
481-
});
491+
})?;
482492

483493
Ok(self)
484494
}
@@ -710,6 +720,7 @@ mod tests {
710720
.with_config_map("configmap")
711721
.build(),
712722
)
723+
.unwrap()
713724
.termination_grace_period(&Duration::from_secs(42))
714725
.unwrap()
715726
.build()

crates/stackable-operator/src/commons/authentication/ldap.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use snafu::{ResultExt, Snafu};
55
use url::{ParseError, Url};
66

77
use crate::{
8-
builder::pod::{container::ContainerBuilder, volume::VolumeMountBuilder, PodBuilder},
8+
builder::{
9+
self,
10+
pod::{container::ContainerBuilder, volume::VolumeMountBuilder, PodBuilder},
11+
},
912
commons::{
1013
authentication::SECRET_BASE_PATH,
1114
networking::HostName,
@@ -16,7 +19,7 @@ use crate::{
1619

1720
pub type Result<T, E = Error> = std::result::Result<T, E>;
1821

19-
#[derive(Debug, PartialEq, Snafu)]
22+
#[derive(Debug, Snafu)]
2023
pub enum Error {
2124
#[snafu(display(
2225
"failed to convert bind credentials (secret class volume) into named Kubernetes volume"
@@ -28,6 +31,14 @@ pub enum Error {
2831

2932
#[snafu(display("failed to add LDAP TLS client details volumes and volume mounts"))]
3033
AddLdapTlsClientDetailsVolumes { source: TlsClientDetailsError },
34+
35+
#[snafu(display("failed to add needed volumes"))]
36+
AddVolumes { source: builder::pod::Error },
37+
38+
#[snafu(display("failed to add needed volumeMounts"))]
39+
AddVolumeMounts {
40+
source: builder::pod::container::Error,
41+
},
3142
}
3243

3344
#[derive(
@@ -99,10 +110,11 @@ impl AuthenticationProvider {
99110
container_builders: Vec<&mut ContainerBuilder>,
100111
) -> Result<()> {
101112
let (volumes, mounts) = self.volumes_and_mounts()?;
102-
pod_builder.add_volumes(volumes);
113+
pod_builder.add_volumes(volumes).context(AddVolumesSnafu)?;
103114

104115
for cb in container_builders {
105-
cb.add_volume_mounts(mounts.clone());
116+
cb.add_volume_mounts(mounts.clone())
117+
.context(AddVolumeMountsSnafu)?;
106118
}
107119

108120
Ok(())

crates/stackable-operator/src/commons/s3/helpers.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use crate::{
1111
};
1212

1313
use super::{
14-
AddS3CredentialVolumesSnafu, AddS3TlsClientDetailsVolumesSnafu, ParseS3EndpointSnafu,
15-
RetrieveS3ConnectionSnafu, S3Bucket, S3BucketSpec, S3Connection, S3ConnectionSpec, S3Error,
16-
SetS3EndpointSchemeSnafu,
14+
AddS3CredentialVolumesSnafu, AddS3TlsClientDetailsVolumesSnafu, AddVolumeMountsSnafu,
15+
AddVolumesSnafu, ParseS3EndpointSnafu, RetrieveS3ConnectionSnafu, S3Bucket, S3BucketSpec,
16+
S3Connection, S3ConnectionSpec, S3Error, SetS3EndpointSchemeSnafu,
1717
};
1818

1919
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
@@ -85,9 +85,10 @@ impl ResolvedS3Connection {
8585
container_builders: Vec<&mut ContainerBuilder>,
8686
) -> Result<(), S3Error> {
8787
let (volumes, mounts) = self.volumes_and_mounts()?;
88-
pod_builder.add_volumes(volumes);
88+
pod_builder.add_volumes(volumes).context(AddVolumesSnafu)?;
8989
for cb in container_builders {
90-
cb.add_volume_mounts(mounts.clone());
90+
cb.add_volume_mounts(mounts.clone())
91+
.context(AddVolumeMountsSnafu)?;
9192
}
9293

9394
Ok(())

crates/stackable-operator/src/commons/s3/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub use helpers::*;
77
use snafu::Snafu;
88
use url::Url;
99

10+
use crate::builder::{self};
11+
1012
use super::{secret_class::SecretClassVolumeError, tls_verification::TlsClientDetailsError};
1113

1214
#[derive(Debug, Snafu)]
@@ -31,4 +33,12 @@ pub enum S3Error {
3133

3234
#[snafu(display("failed to add S3 TLS client details volumes and volume mounts"))]
3335
AddS3TlsClientDetailsVolumes { source: TlsClientDetailsError },
36+
37+
#[snafu(display("failed to add needed volumes"))]
38+
AddVolumes { source: builder::pod::Error },
39+
40+
#[snafu(display("failed to add needed volumeMounts"))]
41+
AddVolumeMounts {
42+
source: builder::pod::container::Error,
43+
},
3444
}

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

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

66
use crate::{
7-
builder::pod::{container::ContainerBuilder, volume::VolumeMountBuilder, PodBuilder},
7+
builder::{
8+
self,
9+
pod::{container::ContainerBuilder, volume::VolumeMountBuilder, PodBuilder},
10+
},
811
commons::{
912
authentication::SECRET_BASE_PATH,
1013
secret_class::{SecretClassVolume, SecretClassVolumeError},
1114
},
1215
};
1316

14-
#[derive(Debug, PartialEq, Snafu)]
17+
#[derive(Debug, Snafu)]
1518
pub enum TlsClientDetailsError {
1619
#[snafu(display("failed to convert secret class volume into named Kubernetes volume"))]
1720
SecretClassVolume { source: SecretClassVolumeError },
21+
22+
#[snafu(display("failed to add needed volumes"))]
23+
AddVolumes { source: builder::pod::Error },
24+
25+
#[snafu(display("failed to add needed volumeMounts"))]
26+
AddVolumeMounts {
27+
source: builder::pod::container::Error,
28+
},
1829
}
1930

2031
#[derive(
@@ -41,10 +52,11 @@ impl TlsClientDetails {
4152
container_builders: Vec<&mut ContainerBuilder>,
4253
) -> Result<(), TlsClientDetailsError> {
4354
let (volumes, mounts) = self.volumes_and_mounts()?;
44-
pod_builder.add_volumes(volumes);
55+
pod_builder.add_volumes(volumes).context(AddVolumesSnafu)?;
4556

4657
for cb in container_builders {
47-
cb.add_volume_mounts(mounts.clone());
58+
cb.add_volume_mounts(mounts.clone())
59+
.context(AddVolumeMountsSnafu)?;
4860
}
4961

5062
Ok(())

0 commit comments

Comments
 (0)