pub struct StructuralGate {
pub applies_to: GateScope,
pub condition: &'static str,
pub blocked_status: &'static str,
pub reason: &'static str,
pub severity: GateSeverity,
}Expand description
One human-actionable structural gate: a condition type, the status that
means BLOCKED, the reason the writer stamps, the CR kinds it appears on,
and how loudly to report it.
Polarity is per-row rather than implied, because kopiur has gates of both
shapes: MoverPermitted=False blocks, and so does DeletionHeld=True.
Fields§
§applies_to: GateScopeThe CR kinds this gate’s condition is written on.
condition: &'static strThe condition type (a consts string, never a literal at the call site).
blocked_status: &'static strThe condition status that means BLOCKED — CONDITION_FALSE or
CONDITION_TRUE.
reason: &'static strThe reason the reconciler stamps when it writes the blocked condition.
severity: GateSeverityHow loudly a tripped gate is reported.
Implementations§
Source§impl StructuralGate
impl StructuralGate
Sourcepub fn matches(&self, condition_type: &str, status: &str, reason: &str) -> bool
pub fn matches(&self, condition_type: &str, status: &str, reason: &str) -> bool
Whether a live condition is this gate: type, status, AND reason
all match.
This is the matcher consumers should reach for. condition + status
alone do not identify a row — CredentialsAvailable=False is written
with several different reasons and therefore has several rows — so
filtering the registry on trips alone yields multiple
hits for one live condition and double-reports it. matches selects exactly one row per
live condition, which is the property
gate_rows_are_unique_and_internally_consistent pins.
use kopiur_api::gates::STRUCTURAL_GATES;
// One live condition off a wedged Snapshot ⇒ exactly one registry row.
let hits: Vec<_> = STRUCTURAL_GATES
.iter()
.filter(|g| g.matches("CredentialsAvailable", "False", "MissingCredentialsSecret"))
.collect();
assert_eq!(hits.len(), 1);
assert_eq!(hits[0].reason, "MissingCredentialsSecret");
// The other reason selects the other row, never both.
assert!(!hits[0].matches("CredentialsAvailable", "False", "MissingServiceAccount"));Sourcepub fn trips(&self, condition_type: &str, status: &str) -> bool
pub fn trips(&self, condition_type: &str, status: &str) -> bool
The reason-agnostic coarse filter: whether a live condition’s
type/status pair is the blocked polarity of this gate’s condition.
The polarity comparison lives here so no consumer re-derives it (the
!= "True" / == "True" mix-ups this registry exists to prevent). Use
it to answer “is this condition one of the ones we gate on at all?” —
notably when a live condition carries a reason NO row covers (a newer
operator’s reason string), where matches would
silently report nothing. To identify WHICH row a condition is, use
matches: trips can match several rows sharing a condition+polarity.
use kopiur_api::gates::{STRUCTURAL_GATES, GateScope};
let mover = STRUCTURAL_GATES
.iter()
.find(|g| g.condition == "MoverPermitted")
.expect("the privileged-mover gate is registered");
assert!(mover.trips("MoverPermitted", "False"));
assert!(!mover.trips("MoverPermitted", "True"));
assert!(!mover.trips("Ready", "False"));
assert_eq!(mover.applies_to, GateScope::SnapshotOrRestore);
// Coarse by design: an unregistered reason still flags the condition.
assert!(mover.trips("MoverPermitted", "False"));Sourcepub fn blocked_is_true(&self) -> bool
pub fn blocked_is_true(&self) -> bool
blocked_status as the bool a condition writer
passes when it writes this gate as BLOCKED — true for
CONDITION_TRUE, false for CONDITION_FALSE.
The controller’s upsert_condition takes a bool, so without this every
registry-driven writer would hand-translate the status string at its own
call site — exactly the per-site re-derivation this registry exists to
remove. Any other string reads as false; the
every_gate_row_is_well_formed tripwire makes that unreachable.
use kopiur_api::gates::STRUCTURAL_GATES;
let mover = STRUCTURAL_GATES
.iter()
.find(|g| g.condition == "MoverPermitted")
.expect("registered");
assert!(!mover.blocked_is_true()); // MoverPermitted=False blocks
let held = STRUCTURAL_GATES
.iter()
.find(|g| g.condition == "DeletionHeld")
.expect("registered");
assert!(held.blocked_is_true()); // DeletionHeld=True blocksTrait Implementations§
Source§impl Clone for StructuralGate
impl Clone for StructuralGate
Source§fn clone(&self) -> StructuralGate
fn clone(&self) -> StructuralGate
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for StructuralGate
Source§impl Debug for StructuralGate
impl Debug for StructuralGate
impl Eq for StructuralGate
Source§impl PartialEq for StructuralGate
impl PartialEq for StructuralGate
Source§fn eq(&self, other: &StructuralGate) -> bool
fn eq(&self, other: &StructuralGate) -> bool
self and other values to be equal, and is used by ==.impl StructuralPartialEq for StructuralGate
Auto Trait Implementations§
impl Freeze for StructuralGate
impl RefUnwindSafe for StructuralGate
impl Send for StructuralGate
impl Sync for StructuralGate
impl Unpin for StructuralGate
impl UnsafeUnpin for StructuralGate
impl UnwindSafe for StructuralGate
Blanket Implementations§
§impl<T> AnyExt for T
impl<T> AnyExt for T
§fn downcast_ref<T>(this: &Self) -> Option<&T>where
T: Any,
fn downcast_ref<T>(this: &Self) -> Option<&T>where
T: Any,
T behind reference§fn downcast_mut<T>(this: &mut Self) -> Option<&mut T>where
T: Any,
fn downcast_mut<T>(this: &mut Self) -> Option<&mut T>where
T: Any,
T behind mutable reference§fn downcast_rc<T>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>>where
T: Any,
fn downcast_rc<T>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>>where
T: Any,
T behind Rc pointer§fn downcast_arc<T>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>>where
T: Any,
fn downcast_arc<T>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>>where
T: Any,
T behind Arc pointer§fn downcast_box<T>(this: Box<Self>) -> Result<Box<T>, Box<Self>>where
T: Any,
fn downcast_box<T>(this: Box<Self>) -> Result<Box<T>, Box<Self>>where
T: Any,
T behind Box pointer§fn downcast_move<T>(this: Self) -> Option<T>
fn downcast_move<T>(this: Self) -> Option<T>
Self to T,
useful only in generic context as a workaround for specializationSource§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T, X> CoerceTo<T> for Xwhere
T: CoerceFrom<X> + ?Sized,
impl<T, X> CoerceTo<T> for Xwhere
T: CoerceFrom<X> + ?Sized,
fn coerce_rc_to(self: Rc<X>) -> Rc<T>
fn coerce_box_to(self: Box<X>) -> Box<T>
fn coerce_ref_to(&self) -> &T
fn coerce_mut_to(&mut self) -> &mut T
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more