Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ url = { version = "2.5.2", features = ["serde"] }
x509-cert = { version = "0.2.5", features = ["builder"] }
zeroize = "1.8.1"

[workspace.lints.clippy]
unwrap_in_result = "deny"
unwrap_used = "deny"
panic = "deny"

# Use O3 in tests to improve the RSA key generation speed in the stackable-certs crate
[profile.test.package]
stackable-certs.opt-level = 3
Expand Down
2 changes: 2 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
allow-unwrap-in-tests = true
allow-panic-in-tests = true
3 changes: 3 additions & 0 deletions crates/k8s-version/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ quote.workspace = true
proc-macro2.workspace = true
serde_yaml.workspace = true
syn.workspace = true

[lints]
workspace = true
8 changes: 8 additions & 0 deletions crates/k8s-version/src/level/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ pub enum Level {
impl FromStr for Level {
type Err = ParseLevelError;

// SAFETY: We purposefully allow the `clippy::unwrap_in_result` lint below in this function.
// We can use expect here, because the correct match labels must be used.
//
// FIXME (@Techassi): This attribute can be used on individual unwrap and expect calls since
// Rust 1.91.0. We should move this attribute to not contaminate an unnecessarily large scope
// once we bump the toolchain to 1.91.0.
// See https://github.com/rust-lang/rust-clippy/pull/15445
#[allow(clippy::unwrap_in_result)]
fn from_str(input: &str) -> Result<Self, Self::Err> {
let captures = LEVEL_REGEX.captures(input).context(InvalidFormatSnafu)?;

Expand Down
9 changes: 9 additions & 0 deletions crates/k8s-version/src/version/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ pub struct Version {
impl FromStr for Version {
type Err = ParseVersionError;

// SAFETY: We purposefully allow the `clippy::unwrap_in_result` lint below in this function.
// We can use expect here, because the correct match label must be used.
//
// FIXME (@Techassi): This attribute can be used on individual unwrap and expect calls since
// Rust 1.91.0. We should move this attribute to not contaminate an unnecessarily large scope
// once we bump the toolchain to 1.91.0.
// See https://github.com/rust-lang/rust-clippy/pull/15445
#[allow(clippy::unwrap_in_result)]
fn from_str(input: &str) -> Result<Self, Self::Err> {
let captures = VERSION_REGEX.captures(input).context(InvalidFormatSnafu)?;

Expand Down Expand Up @@ -141,6 +149,7 @@ mod test {
#[case("v1gamma12", ParseVersionError::ParseLevel { source: ParseLevelError::UnknownIdentifier })]
#[case("v1betä1", ParseVersionError::InvalidFormat)]
#[case("1beta1", ParseVersionError::InvalidFormat)]
#[case("v", ParseVersionError::InvalidFormat)]
#[case("", ParseVersionError::InvalidFormat)]
fn invalid_version(#[case] input: &str, #[case] error: ParseVersionError) {
let err = Version::from_str(input).expect_err("invalid Kubernetes version");
Expand Down
3 changes: 3 additions & 0 deletions crates/stackable-certs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ tokio.workspace = true
tracing.workspace = true
x509-cert.workspace = true
zeroize.workspace = true

[lints]
workspace = true
28 changes: 20 additions & 8 deletions crates/stackable-certs/src/ca/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl PartialEq for Error {
x509_cert::builder::Error::Signature(_),
x509_cert::builder::Error::Signature(_),
) => panic!(
"it is impossible to compare the opaque Error contained witin signature::error::Error"
"it is impossible to compare the opaque Error contained within signature::error::Error"
),
_ => false,
},
Expand Down Expand Up @@ -205,6 +205,16 @@ where
/// validity, this function offers complete control over these parameters.
/// If this level of control is not needed, use [`CertificateAuthority::new`]
/// instead.
//
// SAFETY: We purposefully allow the `clippy::unwrap_in_result` lint below in this function.
// We can use expect here, because the subject name is defined as a constant which must be able
// to be parsed.
//
// FIXME (@Techassi): This attribute can be used on individual unwrap and expect calls since
// Rust 1.91.0. We should move this attribute to not contaminate an unnecessarily large scope
// once we bump the toolchain to 1.91.0.
// See https://github.com/rust-lang/rust-clippy/pull/15445
#[allow(clippy::unwrap_in_result)]
#[instrument(name = "create_certificate_authority_with", skip(signing_key_pair))]
pub fn new_with(signing_key_pair: S, serial_number: u64, validity: Duration) -> Result<Self> {
let serial_number = SerialNumber::from(serial_number);
Expand All @@ -214,7 +224,7 @@ where
// created by us should contain the same subject consisting a common set
// of distinguished names (DNs).
let subject = Name::from_str(SDP_ROOT_CA_SUBJECT)
.expect("the SDP_ROOT_CA_SUBJECT must be a valid subject");
.expect("the constant SDP_ROOT_CA_SUBJECT must be a valid subject");

let spki_pem = signing_key_pair
.verifying_key()
Expand Down Expand Up @@ -511,7 +521,7 @@ mod tests {

#[tokio::test]
async fn rsa_key_generation() {
let mut ca = CertificateAuthority::new_rsa().unwrap();
let mut ca = CertificateAuthority::new_rsa().expect("must be able to create RSA-based CA");
let cert = ca
.generate_rsa_leaf_certificate("Product", "pod", [TEST_SAN], TEST_CERT_LIFETIME)
.expect(
Expand All @@ -523,7 +533,9 @@ mod tests {

#[tokio::test]
async fn ecdsa_key_generation() {
let mut ca = CertificateAuthority::new_ecdsa().unwrap();
let mut ca =
CertificateAuthority::new_ecdsa().expect("must be able to create ECDSA-based CA");

let cert = ca
.generate_ecdsa_leaf_certificate("Product", "pod", [TEST_SAN], TEST_CERT_LIFETIME)
.expect(
Expand All @@ -535,11 +547,11 @@ mod tests {

fn assert_cert_attributes(cert: &Certificate) {
let cert = &cert.tbs_certificate;
let expected_subject = Name::from_str("CN=Product Certificate for pod")
.expect("constant subject must be valid");

// Test subject
assert_eq!(
cert.subject,
Name::from_str("CN=Product Certificate for pod").unwrap()
);
assert_eq!(cert.subject, expected_subject);

// Test SAN extension is present
let extensions = cert.extensions.as_ref().expect("cert must have extensions");
Expand Down
3 changes: 3 additions & 0 deletions crates/stackable-operator-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ syn.workspace = true

[dev-dependencies]
stackable-operator = { path = "../stackable-operator" }

[lints]
workspace = true
3 changes: 3 additions & 0 deletions crates/stackable-operator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ url.workspace = true
indoc.workspace = true
rstest.workspace = true
tempfile.workspace = true

[lints]
workspace = true
32 changes: 13 additions & 19 deletions crates/stackable-operator/src/logging/k8s_events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Utilities for publishing Kubernetes events

use std::error::Error;
use std::{error::Error, fmt::Write};

use kube::runtime::{
controller,
Expand All @@ -12,25 +12,20 @@ use super::controller::ReconcilerError;
/// Converts an [`Error`] into a publishable Kubernetes [`Event`]
fn error_to_event<E: ReconcilerError>(err: &E) -> Event {
// Walk the whole error chain, so that we get all the full reason for the error
let mut full_msg = {
use std::fmt::Write;
let mut buf = err.to_string();
let mut err: &dyn Error = err;
loop {
err = match err.source() {
Some(err) => {
write!(buf, ": {err}").unwrap();
err
}
None => break buf,
}
}
};
message::truncate_with_ellipsis(&mut full_msg, 1024);
let mut error = err.to_string();
let mut source = err.source();

while let Some(err) = source {
write!(error, ": {err}").expect("must be able to concat errors");
source = err.source();
}

message::truncate_with_ellipsis(&mut error, 1024);

Event {
type_: EventType::Warning,
reason: err.category().to_string(),
note: Some(full_msg),
note: Some(error),
action: "Reconcile".to_string(),
secondary: err.secondary_object().map(|secondary| secondary.into()),
}
Expand Down Expand Up @@ -70,8 +65,7 @@ mod message {
pub fn truncate_with_ellipsis(msg: &mut String, max_len: usize) {
const ELLIPSIS: char = '…';
const ELLIPSIS_LEN: usize = ELLIPSIS.len_utf8();
let len = msg.len();
if len > max_len {
if msg.len() > max_len {
let start_of_trunc_char = find_start_of_char(msg, max_len.saturating_sub(ELLIPSIS_LEN));
msg.truncate(start_of_trunc_char);
if ELLIPSIS_LEN <= max_len {
Expand Down
5 changes: 3 additions & 2 deletions crates/stackable-operator/src/product_config_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,12 @@ pub fn validate_all_roles_and_groups_config(
ignore_err: bool,
) -> Result<ValidatedRoleConfigByPropertyKind> {
let mut result = HashMap::new();

for (role, role_group) in role_config {
result.insert(role.to_string(), HashMap::new());
let role_entry = result.entry(role.to_string()).or_insert(HashMap::new());

for (group, properties_by_kind) in role_group {
result.get_mut(role).unwrap().insert(
role_entry.insert(
group.clone(),
validate_role_and_group_config(
version,
Expand Down
3 changes: 3 additions & 0 deletions crates/stackable-shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ time = { workspace = true, optional = true }
[dev-dependencies]
k8s-openapi.workspace = true
rstest.workspace = true

[lints]
workspace = true
24 changes: 15 additions & 9 deletions crates/stackable-shared/src/time/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ mod test {
#[case("15d2m2s1000ms", 1296123)]
#[case("213503982334d", 18446744073657600)]
fn parse_as_secs(#[case] input: &str, #[case] output: u64) {
let dur: Duration = input.parse().unwrap();
let dur: Duration = input.parse().expect("valid duration input must parse");
assert_eq!(dur.as_secs(), output);
}

Expand All @@ -484,7 +484,7 @@ mod test {
#[case("", DurationParseError::EmptyInput)]
#[case("213503982335d", DurationParseError::Overflow { input: "213503982335d".to_string(), value: 213503982335_u128, unit: DurationUnit::Days })]
fn parse_invalid(#[case] input: &str, #[case] expected_err: DurationParseError) {
let err = Duration::from_str(input).unwrap_err();
let err = Duration::from_str(input).expect_err("invalid duration input must not parse");
assert_eq!(err, expected_err)
}

Expand All @@ -495,7 +495,7 @@ mod test {
#[case] input: &str,
#[case] expected_err: DurationParseError,
) {
let err = Duration::from_str(input).unwrap_err();
let err = Duration::from_str(input).expect_err("invalid duration input must produce error");
assert_eq!(err, expected_err)
}

Expand All @@ -506,7 +506,7 @@ mod test {
#[case("1m", None)]
#[case("1s", None)]
fn to_string(#[case] input: &str, #[case] expected: Option<&str>) {
let dur: Duration = input.parse().unwrap();
let dur: Duration = input.parse().expect("valid duration input must parse");
match expected {
Some(e) => assert_eq!(dur.to_string(), e),
None => assert_eq!(dur.to_string(), input),
Expand All @@ -520,7 +520,7 @@ mod test {
dur: Duration,
}

let s: S = serde_yaml::from_str("dur: 15d2m2s").unwrap();
let s: S = serde_yaml::from_str("dur: 15d2m2s").expect("valid duration must deserialize");
assert_eq!(s.dur.as_secs(), 1296122);
}

Expand All @@ -532,14 +532,20 @@ mod test {
}

let s = S {
dur: "15d2m2s".parse().unwrap(),
dur: "15d2m2s"
.parse()
.expect("static string must be valid duration"),
};
assert_eq!(serde_yaml::to_string(&s).unwrap(), "dur: 15d2m2s\n");

assert_eq!(
serde_yaml::to_string(&s).expect("valid duration must serialize"),
"dur: 15d2m2s\n"
);
}

#[test]
fn add_ops() {
let mut dur1 = Duration::from_str("20s").unwrap();
let mut dur1 = Duration::from_str("20s").expect("static string must be valid duration");
let dur2 = Duration::from_secs(10);

let dur = dur1 + dur2;
Expand All @@ -551,7 +557,7 @@ mod test {

#[test]
fn sub_ops() {
let mut dur1 = Duration::from_str("20s").unwrap();
let mut dur1 = Duration::from_str("20s").expect("static string must be valid duration");
let dur2 = Duration::from_secs(10);

let dur = dur1 - dur2;
Expand Down
3 changes: 3 additions & 0 deletions crates/stackable-telemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ tracing-opentelemetry.workspace = true
rstest.workspace = true
stackable-webhook = { path = "../stackable-webhook" }

[lints]
workspace = true

[package.metadata.cargo-udeps.ignore]
# Required for doc tests in stackable-telemetry
development = ["stackable-webhook"]
10 changes: 10 additions & 0 deletions crates/stackable-telemetry/src/tracing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,16 @@ impl Tracing {
/// Name the guard variable appropriately, do not just use <code>let _ =</code>, as that will drop
/// immediately.
/// </div>
//
// SAFETY: We purposefully allow the `clippy::unwrap_in_result` lint below in this function.
// We can use expect here, because the directives are defined as a constant value which must be
// able to be parsed.
//
// FIXME (@Techassi): This attribute can be used on individual unwrap and expect calls since
// Rust 1.91.0. We should move this attribute to not contaminate an unnecessarily large scope
// once we bump the toolchain to 1.91.0.
// See https://github.com/rust-lang/rust-clippy/pull/15445
#[allow(clippy::unwrap_in_result)]
pub fn init(mut self) -> Result<Tracing> {
let mut layers: Vec<Box<dyn Layer<Registry> + Sync + Send>> = Vec::new();

Expand Down
3 changes: 3 additions & 0 deletions crates/stackable-versioned-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,6 @@ serde_yaml.workspace = true
snafu.workspace = true
tracing.workspace = true
trybuild.workspace = true

[lints]
workspace = true
13 changes: 7 additions & 6 deletions crates/stackable-versioned-macros/src/attrs/item/field.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use darling::{Error, FromField, FromMeta, Result, util::Flag};
use syn::{Attribute, Ident};
use syn::{Attribute, Ident, spanned::Spanned};

use crate::{
attrs::item::CommonItemAttributes,
Expand Down Expand Up @@ -58,11 +58,12 @@ impl FieldAttributes {
///
/// Internally, it calls out to other specialized validation functions.
fn validate(self) -> Result<Self> {
let ident = self
.ident
.as_ref()
.expect("internal error: field must have an ident")
.clone();
let field_span = self.ident.span();

let ident = self.ident.clone().ok_or_else(|| {
darling::Error::custom("internal error: field must have an ident")
.with_span(&field_span)
})?;

self.common
.validate(FieldIdents::from(ident), &self.attrs)?;
Expand Down
Loading