mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-18 02:33:15 +00:00
a9691b6797
backlog#1823 step 10, batch 1 of the repo-wide item-allow sweep. 227 bare #[allow(dead_code)] remain across 83 files; this takes the 19 in utils, notify, checksums, policy, keystone and trusted-proxies, which are small enough to verify end to end. Removing all 19 first, before writing any reason, matters: 8 of them suppress nothing. Every allow in utils, one in policy and three in notify sit on items that are publicly reachable, so dead_code never applied to them — the same shape as the swift module and kms's dek.rs. Writing a reason onto a no-op allow would dress noise up as considered judgement, so those are simply deleted. Three items are genuinely dead and go with their allows: notify's new_target_id_set, the AWS metadata fetcher's get_metadata_token, and policy's empty `pub struct Value;`, none of which is referenced anywhere in the tree. The remaining eight keep an allow, now saying why the item survives rather than who calls it. Two are exercised only by their own crate's tests (checksums' MD5_HEADER_NAME, policy's is_match_as_pattern_prefix). Four are fields written but never read back: keystone's verify_ssl, parsed from config after the reqwest client is already built; keystone's client handle, which keeps the Keystone client alive for the mapper's lifetime; the AWS IMDS endpoint, kept beside the client while requests build their own URLs; and notify's rules_map, whose own comment retains it for snapshot-time judgements no code performs. checksums' Md5 needed the most care. Crc32, Sha256 and seven others each have an arm in ChecksumAlgorithm::into_impl, and Md5 has none, which reads like a missing algorithm. It is not: ChecksumAlgorithm has no Md5 variant at all. S3 carries Content-MD5 as its own header, separate from the x-amz-checksum-* family, and this impl exists so both paths share the Checksum trait. The reason records that, so the next reader does not re-derive it. One measurement note for anyone continuing this sweep: cargo does not re-emit warnings for cached compilations, so a per-crate loop of `cargo check -p <crate>` under-reports. checksums showed zero that way while actually carrying three. Touch the sources and check the crates in one invocation, then attribute by path. Verification: the six crates are warning-free under cargo check --tests; clippy --lib --tests -D warnings clean; cargo nextest run 1096 passed; make pre-commit exit 0. Ref rustfs/backlog#1823 (step 10).
195 lines
7.5 KiB
Rust
195 lines
7.5 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use super::rules_map::RulesMap;
|
|
use super::xml_config::ParseConfigError as BucketNotificationConfigError;
|
|
use crate::rules::NotificationConfiguration;
|
|
use crate::rules::subscriber_snapshot::{BucketRulesSnapshot, DynRulesContainer, RuleEvents, RulesContainer};
|
|
use rustfs_s3_types::EventName;
|
|
use rustfs_targets::arn::TargetID;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::io::Read;
|
|
use std::sync::Arc;
|
|
|
|
/// A "rule view", only used for snapshot mask/consistency verification.
|
|
/// Here we choose to generate the view by "single event" to ensure that event_mask calculation is reliable and simple.
|
|
#[derive(Debug)]
|
|
struct RuleView {
|
|
events: Vec<EventName>,
|
|
}
|
|
|
|
impl RuleEvents for RuleView {
|
|
fn subscribed_events(&self) -> &[EventName] {
|
|
&self.events
|
|
}
|
|
}
|
|
|
|
/// Adapt RulesMap to RulesContainer.
|
|
/// Key point: The items returned by iter_rules are &dyn RuleEvents, so a RuleView list is cached in the container.
|
|
#[derive(Debug)]
|
|
struct CompiledRules {
|
|
// Keep RulesMap (can be used later if you want to make more complex judgments during the snapshot reading phase)
|
|
#[allow(
|
|
dead_code,
|
|
reason = "speculative retention: the comment above keeps it for richer snapshot-time judgements that no code performs yet (backlog#1823)"
|
|
)]
|
|
rules_map: RulesMap,
|
|
// for RulesContainer::iter_rules
|
|
rule_views: Vec<RuleView>,
|
|
}
|
|
|
|
impl CompiledRules {
|
|
fn from_rules_map(rules_map: &RulesMap) -> Self {
|
|
let mut rule_views = Vec::new();
|
|
|
|
for ev in rules_map.iter_events() {
|
|
rule_views.push(RuleView { events: vec![ev] });
|
|
}
|
|
|
|
Self {
|
|
rules_map: rules_map.clone(),
|
|
rule_views,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl RulesContainer for CompiledRules {
|
|
type Rule = dyn RuleEvents;
|
|
|
|
fn iter_rules<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::Rule> + 'a> {
|
|
// Key: Convert &RuleView into &dyn RuleEvents
|
|
Box::new(self.rule_views.iter().map(|v| v as &dyn RuleEvents))
|
|
}
|
|
}
|
|
|
|
/// Configuration for bucket notifications.
|
|
/// This struct now holds the parsed and validated rules in the new RulesMap format.
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct BucketNotificationConfig {
|
|
pub region: String, // Region where this config is applicable
|
|
pub rules: RulesMap, // The new, more detailed RulesMap
|
|
}
|
|
|
|
impl BucketNotificationConfig {
|
|
pub fn new(region: &str) -> Self {
|
|
BucketNotificationConfig {
|
|
region: region.to_string(),
|
|
rules: RulesMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Adds a rule to the configuration.
|
|
/// This method allows adding a rule with a specific event and target ID.
|
|
pub fn add_rule(
|
|
&mut self,
|
|
event_names: &[EventName], // Assuming event_names is a list of event names
|
|
pattern: String, // The object key pattern for the rule
|
|
target_id: TargetID, // The target ID for the notification
|
|
) {
|
|
self.rules.add_rule_config(event_names, pattern, target_id);
|
|
}
|
|
|
|
/// Parses notification configuration from XML.
|
|
/// `arn_list` is a list of valid ARN strings for validation.
|
|
pub fn from_xml<R: Read + std::io::BufRead>(
|
|
reader: R,
|
|
current_region: &str,
|
|
arn_list: &[String],
|
|
) -> Result<Self, BucketNotificationConfigError> {
|
|
let mut parsed_config = NotificationConfiguration::from_reader(reader)?;
|
|
|
|
// Set defaults (region in ARNs if empty, xmlns) before validation
|
|
parsed_config.set_defaults(current_region);
|
|
|
|
// Validate the parsed configuration
|
|
parsed_config.validate(current_region, arn_list)?;
|
|
|
|
let mut rules_map = RulesMap::new();
|
|
for queue_conf in parsed_config.queue_list {
|
|
// The ARN in queue_conf should now have its region set if it was originally empty.
|
|
// Ensure TargetID can be cloned or extracted correctly.
|
|
let target_id = queue_conf.arn.target_id.clone();
|
|
let pattern_str = queue_conf.filter.pattern();
|
|
rules_map.add_rule_config(&queue_conf.events, pattern_str, target_id);
|
|
}
|
|
|
|
Ok(BucketNotificationConfig {
|
|
region: current_region.to_string(), // Config is for the current_region
|
|
rules: rules_map,
|
|
})
|
|
}
|
|
|
|
/// Validates the *current* BucketNotificationConfig.
|
|
/// This might be redundant if construction always implies validation.
|
|
/// However, Go's Config has a Validate method.
|
|
/// The primary validation now happens during `from_xml` via `NotificationConfiguration::validate`.
|
|
/// This method could re-check against an updated arn_list or region if needed.
|
|
pub fn validate(&self, current_region: &str, arn_list: &[String]) -> Result<(), BucketNotificationConfigError> {
|
|
if self.region != current_region {
|
|
return Err(BucketNotificationConfigError::RegionMismatch {
|
|
config_region: self.region.clone(),
|
|
current_region: current_region.to_string(),
|
|
});
|
|
}
|
|
|
|
// Iterate through the rules in self.rules and validate their TargetIDs against arn_list
|
|
// This requires RulesMap to expose its internal structure or provide an iterator
|
|
for (_event_name, pattern_rules) in self.rules.inner().iter() {
|
|
for (_pattern, target_id_set) in pattern_rules.inner().iter() {
|
|
// Assuming PatternRules has inner()
|
|
for target_id in target_id_set {
|
|
// Construct the ARN string for this target_id and self.region
|
|
let arn_to_check = target_id.to_arn(&self.region); // Assuming TargetID has to_arn
|
|
if !arn_list.contains(&arn_to_check.to_string()) {
|
|
return Err(BucketNotificationConfigError::ArnNotFound(arn_to_check.to_string()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Expose the RulesMap for the notifier
|
|
pub fn get_rules_map(&self) -> &RulesMap {
|
|
&self.rules
|
|
}
|
|
|
|
/// Sets the region for the configuration
|
|
pub fn set_region(&mut self, region: &str) {
|
|
self.region = region.to_string();
|
|
}
|
|
|
|
/// Compiles the current BucketNotificationConfig into a BucketRulesSnapshot.
|
|
/// This involves transforming the rules into a format suitable for runtime use,
|
|
/// and calculating the event mask based on the subscribed events of the rules.
|
|
///
|
|
/// # Returns
|
|
/// A BucketRulesSnapshot containing the compiled rules and event mask.
|
|
pub fn compile_snapshot(&self) -> BucketRulesSnapshot<DynRulesContainer> {
|
|
// 1) Generate container from RulesMap
|
|
let compiled = CompiledRules::from_rules_map(self.get_rules_map());
|
|
let rules: Arc<DynRulesContainer> = Arc::new(compiled) as Arc<DynRulesContainer>;
|
|
|
|
// 2) Calculate event_mask
|
|
let mut mask = 0u64;
|
|
for rule in rules.iter_rules() {
|
|
for ev in rule.subscribed_events() {
|
|
mask |= ev.mask();
|
|
}
|
|
}
|
|
|
|
BucketRulesSnapshot { event_mask: mask, rules }
|
|
}
|
|
}
|