From 2ebdc199ac65efebe0ab4240e39f02041fd353a4 Mon Sep 17 00:00:00 2001 From: David Morrison Date: Thu, 21 Nov 2024 09:42:57 -0800 Subject: [PATCH] feat: fix issues discovered by validation --- Cargo.lock | 2 + sk-cli/Cargo.toml | 2 + sk-cli/src/validation/annotated_trace.rs | 65 ++++++++++++++++++- sk-cli/src/validation/mod.rs | 25 ++++++- .../src/validation/status_field_populated.rs | 10 +++ sk-cli/src/validation/tests/mod.rs | 5 ++ .../tests/status_field_populated_test.rs | 1 - .../validation/tests/validation_store_test.rs | 2 +- sk-cli/src/validation/validation_store.rs | 33 +++++++++- sk-cli/src/validation/validator.rs | 6 ++ sk-cli/src/xray/app.rs | 4 +- sk-cli/src/xray/tests/view_test.rs | 2 +- sk-cli/src/xray/view/helpers.rs | 1 - sk-core/src/k8s/gvk.rs | 6 +- sk-core/src/k8s/tests/util_test.rs | 1 - sk-core/src/k8s/testutils/objs.rs | 1 - sk-core/src/k8s/util.rs | 6 +- sk-core/src/lib.rs | 4 ++ sk-ctrl/src/cert_manager.rs | 1 - sk-driver/src/tests/mutation_test.rs | 1 - sk-store/src/event_list.rs | 4 +- sk-store/src/filter.rs | 1 - sk-store/src/index.rs | 2 +- sk-store/src/lib.rs | 2 + sk-store/src/pod_owners_map.rs | 2 +- sk-store/src/store.rs | 22 +++++-- sk-store/src/tests/import_export_test.rs | 2 - sk-store/src/tests/trace_store_test.rs | 1 - sk-store/src/watchers/dyn_obj_watcher.rs | 2 +- sk-store/src/watchers/tests/mod.rs | 1 - 30 files changed, 176 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 007ce57f..7b770a6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3827,6 +3827,7 @@ version = "2.0.0" dependencies = [ "anyhow", "assertables", + "bytes", "chrono", "clap", "clap_complete", @@ -3840,6 +3841,7 @@ dependencies = [ "lazy_static", "ratatui", "reqwest", + "rmp-serde", "rstest", "serde", "serde_json", diff --git a/sk-cli/Cargo.toml b/sk-cli/Cargo.toml index ad68c287..b33d717d 100644 --- a/sk-cli/Cargo.toml +++ b/sk-cli/Cargo.toml @@ -13,6 +13,7 @@ testutils = [] [dependencies] anyhow = { workspace = true } +bytes = { workspace = true } chrono = { workspace = true } clockabilly = { workspace = true } clap = { workspace = true } @@ -25,6 +26,7 @@ k8s-openapi = { workspace = true } lazy_static = { workspace = true } ratatui = { workspace = true } reqwest = { workspace = true } +rmp-serde = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } diff --git a/sk-cli/src/validation/annotated_trace.rs b/sk-cli/src/validation/annotated_trace.rs index 1321c052..9e91647b 100644 --- a/sk-cli/src/validation/annotated_trace.rs +++ b/sk-cli/src/validation/annotated_trace.rs @@ -4,11 +4,15 @@ use std::collections::{ }; use std::slice; -use kube::api::DynamicObject; +use json_patch_ext::{ + patch_ext, + PatchOperation, +}; use sk_core::external_storage::{ ObjectStoreWrapper, SkObjectStore, }; +use sk_core::prelude::*; use sk_store::{ TraceEvent, TraceStorable, @@ -32,13 +36,30 @@ impl AnnotatedTraceEvent { AnnotatedTraceEvent { data, annotations } } + + pub fn clear_annotations(&mut self) { + self.annotations = vec![vec![]; self.data.len()]; + } +} + +pub enum PatchLocations { + Everywhere, + #[allow(dead_code)] + ObjectReference(TypeMeta, String), +} + +pub struct AnnotatedTracePatch { + pub locations: PatchLocations, + pub op: PatchOperation, } #[derive(Default)] pub struct AnnotatedTrace { + path: String, #[allow(dead_code)] base: TraceStore, - path: String, + patches: Vec, + events: Vec, summary: BTreeMap, } @@ -57,8 +78,37 @@ impl AnnotatedTrace { }) } + pub fn apply_patch(&mut self, patch: AnnotatedTracePatch) -> EmptyResult { + for event in self.events.iter_mut() { + for obj in event.data.applied_objs.iter_mut() { + let should_apply_here = match patch.locations { + PatchLocations::Everywhere => true, + PatchLocations::ObjectReference(ref type_, ref ns_name) => { + obj.types.as_ref().is_some_and(|t| t == type_) && &obj.namespaced_name() == ns_name + }, + }; + + if should_apply_here { + patch_ext(&mut obj.data, patch.op.clone())?; + } + } + } + self.patches.push(patch); + + Ok(()) + } + + pub fn export(&self) -> anyhow::Result> { + let trace = self + .base + .clone_with_events(self.events.iter().map(|a_event| a_event.data.clone()).collect()); + trace.export_all() + } + pub fn validate(&mut self, validators: &mut BTreeMap) { + self.summary.clear(); for event in self.events.iter_mut() { + event.clear_annotations(); for (code, validator) in validators.iter_mut() { let affected_indices = validator.check_next_event(event); let count = affected_indices.len(); @@ -75,6 +125,17 @@ impl AnnotatedTrace { self.events.get(idx) } + pub fn get_next_error(&self) -> Option { + for event in &self.events { + for annotation in &event.annotations { + if let Some(code) = annotation.first() { + return Some(*code); + } + } + } + None + } + pub fn get_object(&self, event_idx: usize, obj_idx: usize) -> Option<&DynamicObject> { let event = self.events.get(event_idx)?; let applied_len = event.data.applied_objs.len(); diff --git a/sk-cli/src/validation/mod.rs b/sk-cli/src/validation/mod.rs index 137d7a5f..2f40820d 100644 --- a/sk-cli/src/validation/mod.rs +++ b/sk-cli/src/validation/mod.rs @@ -3,16 +3,23 @@ mod status_field_populated; mod validation_store; mod validator; +use bytes::Bytes; use clap::{ value_parser, Subcommand, ValueEnum, }; +use sk_core::external_storage::{ + ObjectStoreWrapper, + SkObjectStore, +}; use sk_core::prelude::*; pub use self::annotated_trace::{ AnnotatedTrace, AnnotatedTraceEvent, + AnnotatedTracePatch, + PatchLocations, }; pub use self::validation_store::ValidationStore; pub use self::validator::{ @@ -36,6 +43,17 @@ pub enum ValidateSubcommand { pub struct CheckArgs { #[arg(long_help = "location of the input trace file")] pub trace_path: String, + + #[arg(long, long_help = "fix all discovered issues")] + pub fix: bool, + + #[arg( + short, + long, + long_help = "output path for modified trace (REQUIRED if --fix is set)", + required_if_eq("fix", "true") + )] + pub output: Option, } #[derive(clap::Args)] @@ -69,7 +87,12 @@ pub async fn cmd(subcommand: &ValidateSubcommand) -> EmptyResult { match subcommand { ValidateSubcommand::Check(args) => { let mut trace = AnnotatedTrace::new(&args.trace_path).await?; - validators.validate_trace(&mut trace); + validators.validate_trace(&mut trace, args.fix)?; + if let Some(output_path) = &args.output { + let trace_data = trace.export()?; + let object_store = SkObjectStore::new(output_path)?; + object_store.put(Bytes::from(trace_data)).await?; + } print_summary(&trace, &validators)?; }, ValidateSubcommand::Explain(args) => validators.explain(&args.code)?, diff --git a/sk-cli/src/validation/status_field_populated.rs b/sk-cli/src/validation/status_field_populated.rs index 0c6a8490..970a3e51 100644 --- a/sk-cli/src/validation/status_field_populated.rs +++ b/sk-cli/src/validation/status_field_populated.rs @@ -1,3 +1,9 @@ +use json_patch_ext::{ + format_ptr, + remove_operation, + PatchOperation, +}; + use super::annotated_trace::AnnotatedTraceEvent; use super::validator::{ Diagnostic, @@ -35,6 +41,10 @@ impl Diagnostic for StatusFieldPopulated { .collect() } + fn fixes(&self) -> Vec { + vec![remove_operation(format_ptr!("/status"))] + } + fn reset(&mut self) {} } diff --git a/sk-cli/src/validation/tests/mod.rs b/sk-cli/src/validation/tests/mod.rs index 0b124bc7..e8cd67ac 100644 --- a/sk-cli/src/validation/tests/mod.rs +++ b/sk-cli/src/validation/tests/mod.rs @@ -3,6 +3,7 @@ mod validation_store_test; use std::collections::BTreeMap; +use json_patch_ext::PatchOperation; use rstest::*; use sk_core::prelude::*; use sk_store::TraceEvent; @@ -55,6 +56,10 @@ impl Diagnostic for TestDiagnostic { } } + fn fixes(&self) -> Vec { + vec![] + } + fn reset(&mut self) {} } diff --git a/sk-cli/src/validation/tests/status_field_populated_test.rs b/sk-cli/src/validation/tests/status_field_populated_test.rs index 2dc18735..3013cd0c 100644 --- a/sk-cli/src/validation/tests/status_field_populated_test.rs +++ b/sk-cli/src/validation/tests/status_field_populated_test.rs @@ -1,5 +1,4 @@ use assertables::*; -use kube::api::DynamicObject; use serde_json::json; use sk_store::TraceEvent; diff --git a/sk-cli/src/validation/tests/validation_store_test.rs b/sk-cli/src/validation/tests/validation_store_test.rs index 17414e4b..860902cc 100644 --- a/sk-cli/src/validation/tests/validation_store_test.rs +++ b/sk-cli/src/validation/tests/validation_store_test.rs @@ -4,7 +4,7 @@ use super::*; #[rstest] fn test_validate_trace(mut test_validation_store: ValidationStore, mut annotated_trace: AnnotatedTrace) { - test_validation_store.validate_trace(&mut annotated_trace); + test_validation_store.validate_trace(&mut annotated_trace, false).unwrap(); for evt in annotated_trace.iter() { if evt.data.applied_objs.len() > 1 { diff --git a/sk-cli/src/validation/validation_store.rs b/sk-cli/src/validation/validation_store.rs index bcaf981a..8d9f0731 100644 --- a/sk-cli/src/validation/validation_store.rs +++ b/sk-cli/src/validation/validation_store.rs @@ -4,13 +4,15 @@ use anyhow::anyhow; use serde::Serialize; use sk_core::prelude::*; -use super::annotated_trace::AnnotatedTrace; use super::validator::{ Validator, ValidatorCode, }; use super::{ status_field_populated, + AnnotatedTrace, + AnnotatedTracePatch, + PatchLocations, PrintFormat, }; @@ -20,12 +22,37 @@ pub struct ValidationStore { } impl ValidationStore { - pub fn validate_trace(&mut self, trace: &mut AnnotatedTrace) { + pub fn validate_trace(&mut self, trace: &mut AnnotatedTrace, fix: bool) -> EmptyResult { for validator in self.validators.values_mut() { validator.reset(); } - trace.validate(&mut self.validators); + loop { + trace.validate(&mut self.validators); + + if !fix { + break; + } + + let Some(next_error) = trace.get_next_error() else { + break; + }; + + let Some(op) = self + .validators + .get(&next_error) + .ok_or(anyhow!("validation error"))? + .fixes() + .first() + .cloned() + else { + println!("no fix available for {next_error}; continuing"); + break; + }; + trace.apply_patch(AnnotatedTracePatch { locations: PatchLocations::Everywhere, op })?; + } + + Ok(()) } pub(super) fn explain(&self, code: &ValidatorCode) -> EmptyResult { diff --git a/sk-cli/src/validation/validator.rs b/sk-cli/src/validation/validator.rs index fad1ef7a..6c04df10 100644 --- a/sk-cli/src/validation/validator.rs +++ b/sk-cli/src/validation/validator.rs @@ -2,6 +2,7 @@ use std::fmt; use std::str::from_utf8; use anyhow::bail; +use json_patch_ext::PatchOperation; use serde::{ Serialize, Serializer, @@ -51,6 +52,7 @@ impl fmt::Display for ValidatorCode { pub trait Diagnostic { fn check_next_event(&mut self, evt: &mut AnnotatedTraceEvent) -> Vec; + fn fixes(&self) -> Vec; fn reset(&mut self); } @@ -72,6 +74,10 @@ impl Validator { self.diagnostic.check_next_event(a_event) } + pub fn fixes(&self) -> Vec { + self.diagnostic.fixes() + } + pub fn reset(&mut self) { self.diagnostic.reset() } diff --git a/sk-cli/src/xray/app.rs b/sk-cli/src/xray/app.rs index f93138d6..9660e21e 100644 --- a/sk-cli/src/xray/app.rs +++ b/sk-cli/src/xray/app.rs @@ -47,7 +47,9 @@ impl App { } pub(super) fn rebuild_annotated_trace(&mut self) { - self.validation_store.validate_trace(&mut self.annotated_trace) + self.validation_store + .validate_trace(&mut self.annotated_trace, false) + .expect("validation failed"); } pub(super) fn update_state(&mut self, msg: Message) -> bool { diff --git a/sk-cli/src/xray/tests/view_test.rs b/sk-cli/src/xray/tests/view_test.rs index 5b2f3729..bc4556af 100644 --- a/sk-cli/src/xray/tests/view_test.rs +++ b/sk-cli/src/xray/tests/view_test.rs @@ -15,7 +15,7 @@ use crate::validation::{ #[fixture] fn test_app(mut test_validation_store: ValidationStore, mut annotated_trace: AnnotatedTrace) -> App { - test_validation_store.validate_trace(&mut annotated_trace); + test_validation_store.validate_trace(&mut annotated_trace, false).unwrap(); App { annotated_trace, event_list_state: ListState::default().with_selected(Some(0)), diff --git a/sk-cli/src/xray/view/helpers.rs b/sk-cli/src/xray/view/helpers.rs index 380c7298..7c62800d 100644 --- a/sk-cli/src/xray/view/helpers.rs +++ b/sk-cli/src/xray/view/helpers.rs @@ -1,5 +1,4 @@ use chrono::TimeDelta; -use kube::api::DynamicObject; use lazy_static::lazy_static; use ratatui::prelude::*; use sk_core::prelude::*; diff --git a/sk-core/src/k8s/gvk.rs b/sk-core/src/k8s/gvk.rs index d89b9119..0a10275e 100644 --- a/sk-core/src/k8s/gvk.rs +++ b/sk-core/src/k8s/gvk.rs @@ -2,11 +2,7 @@ use std::borrow::Cow; use std::fmt; use std::ops::Deref; -use kube::api::{ - DynamicObject, - GroupVersionKind, - TypeMeta, -}; +use kube::api::GroupVersionKind; use serde::{ de, Deserialize, diff --git a/sk-core/src/k8s/tests/util_test.rs b/sk-core/src/k8s/tests/util_test.rs index 7f748ec1..0c3627b7 100644 --- a/sk-core/src/k8s/tests/util_test.rs +++ b/sk-core/src/k8s/tests/util_test.rs @@ -1,5 +1,4 @@ use clockabilly::Utc; -use kube::api::DynamicObject; use serde_json as json; use super::*; diff --git a/sk-core/src/k8s/testutils/objs.rs b/sk-core/src/k8s/testutils/objs.rs index 5aa60031..b06f271a 100644 --- a/sk-core/src/k8s/testutils/objs.rs +++ b/sk-core/src/k8s/testutils/objs.rs @@ -1,4 +1,3 @@ -use kube::api::DynamicObject; use kube::discovery::ApiResource; use rstest::*; use serde_json::json; diff --git a/sk-core/src/k8s/util.rs b/sk-core/src/k8s/util.rs index 4d352039..6ba8a578 100644 --- a/sk-core/src/k8s/util.rs +++ b/sk-core/src/k8s/util.rs @@ -1,10 +1,6 @@ use std::collections::BTreeMap; -use kube::api::{ - DynamicObject, - Resource, - TypeMeta, -}; +use kube::api::Resource; use serde_json as json; use super::*; diff --git a/sk-core/src/lib.rs b/sk-core/src/lib.rs index 65f5e2b9..2d53088d 100644 --- a/sk-core/src/lib.rs +++ b/sk-core/src/lib.rs @@ -11,6 +11,10 @@ pub mod time; pub mod prelude { pub use k8s_openapi::api::core::v1 as corev1; pub use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1; + pub use kube::api::{ + DynamicObject, + TypeMeta, + }; pub use kube::{ CustomResourceExt, ResourceExt, diff --git a/sk-ctrl/src/cert_manager.rs b/sk-ctrl/src/cert_manager.rs index 69b2b480..afc8e9b7 100644 --- a/sk-ctrl/src/cert_manager.rs +++ b/sk-ctrl/src/cert_manager.rs @@ -1,6 +1,5 @@ use std::collections::BTreeMap; -use kube::api::TypeMeta; use kube::discovery::ApiResource; use schemars::JsonSchema; use serde::{ diff --git a/sk-driver/src/tests/mutation_test.rs b/sk-driver/src/tests/mutation_test.rs index 31abb344..b4f89d2f 100644 --- a/sk-driver/src/tests/mutation_test.rs +++ b/sk-driver/src/tests/mutation_test.rs @@ -4,7 +4,6 @@ use json_patch_ext::{ patch_ext, Patch, }; -use kube::api::TypeMeta; use kube::core::admission::{ AdmissionRequest, AdmissionResponse, diff --git a/sk-store/src/event_list.rs b/sk-store/src/event_list.rs index ee572e57..20422a89 100644 --- a/sk-store/src/event_list.rs +++ b/sk-store/src/event_list.rs @@ -1,7 +1,6 @@ use std::collections::VecDeque; use std::ops::Index; -use kube::api::DynamicObject; use sk_core::prelude::*; use tracing::*; @@ -10,7 +9,7 @@ use crate::{ TraceEvent, }; -#[derive(Default)] +#[derive(Clone, Default)] pub struct TraceEventList(VecDeque); impl TraceEventList { @@ -78,7 +77,6 @@ impl From> for TraceEventList { } } -#[cfg(test)] impl FromIterator for TraceEventList { fn from_iter>(ii: T) -> Self { TraceEventList(ii.into_iter().collect()) diff --git a/sk-store/src/filter.rs b/sk-store/src/filter.rs index d2e7b619..7dec10d5 100644 --- a/sk-store/src/filter.rs +++ b/sk-store/src/filter.rs @@ -1,4 +1,3 @@ -use kube::api::DynamicObject; use sk_api::v1::ExportFilters; use sk_core::prelude::*; diff --git a/sk-store/src/index.rs b/sk-store/src/index.rs index 8dfd893a..718dcd02 100644 --- a/sk-store/src/index.rs +++ b/sk-store/src/index.rs @@ -10,7 +10,7 @@ use sk_core::k8s::{ GVK, }; -#[derive(Default, Deserialize, Serialize)] +#[derive(Clone, Default, Deserialize, Serialize)] pub struct TraceIndex { #[serde(flatten)] index: HashMap>, diff --git a/sk-store/src/lib.rs b/sk-store/src/lib.rs index 34305de3..3af6d1c0 100644 --- a/sk-store/src/lib.rs +++ b/sk-store/src/lib.rs @@ -57,6 +57,8 @@ pub struct TraceIterator<'a> { idx: usize, } +const CURRENT_TRACE_FORMAT_VERSION: u16 = 2; + #[derive(Deserialize, Serialize)] pub struct ExportedTrace { version: u16, diff --git a/sk-store/src/pod_owners_map.rs b/sk-store/src/pod_owners_map.rs index b78b7975..035e20c2 100644 --- a/sk-store/src/pod_owners_map.rs +++ b/sk-store/src/pod_owners_map.rs @@ -50,7 +50,7 @@ use crate::TraceIndex; pub type PodLifecyclesMap = HashMap>; -#[derive(Default)] +#[derive(Clone, Default)] pub(crate) struct PodOwnersMap { m: HashMap<(GVK, String), PodLifecyclesMap>, index: HashMap, diff --git a/sk-store/src/store.rs b/sk-store/src/store.rs index 2e46db8d..baf38935 100644 --- a/sk-store/src/store.rs +++ b/sk-store/src/store.rs @@ -5,7 +5,6 @@ use clockabilly::{ Clockable, UtcClock, }; -use kube::api::DynamicObject; use sk_api::v1::ExportFilters; use sk_core::jsonutils; use sk_core::k8s::{ @@ -30,9 +29,9 @@ use crate::{ TraceIndex, TraceIterator, TraceStorable, + CURRENT_TRACE_FORMAT_VERSION, }; -const CURRENT_TRACE_VERSION: u16 = 2; #[derive(Debug, Error)] pub enum TraceStoreError { @@ -43,7 +42,7 @@ pub enum TraceStoreError { ParseFailed(#[from] rmp_serde::decode::Error), } -#[derive(Default)] +#[derive(Clone, Default)] pub struct TraceStore { pub(crate) config: TracerConfig, pub(crate) events: TraceEventList, @@ -63,6 +62,12 @@ impl TraceStore { TraceStore { config, ..Default::default() } } + pub fn clone_with_events(&self, events: TraceEventList) -> TraceStore { + let mut store = self.clone(); + store.events = events; + store + } + pub fn export(&self, start_ts: i64, end_ts: i64, filter: &ExportFilters) -> anyhow::Result> { info!("Exporting objs between {start_ts} and {end_ts} with filters: {filter:?}"); @@ -77,7 +82,7 @@ impl TraceStore { // owned by some object contained in the trace let pod_lifecycles = self.pod_owners.filter(start_ts, end_ts, &index); let data = rmp_serde::to_vec_named(&ExportedTrace { - version: CURRENT_TRACE_VERSION, + version: CURRENT_TRACE_FORMAT_VERSION, config: self.config.clone(), events, index, @@ -88,13 +93,20 @@ impl TraceStore { Ok(data) } + pub fn export_all(&self) -> anyhow::Result> { + let (Some(start_ts), Some(end_ts)) = (self.start_ts(), self.end_ts()) else { + return Ok(vec![]); + }; + self.export(start_ts, end_ts, &ExportFilters::default()) + } + // Note that _importing_ data into a trace store is lossy -- we don't store (or import) all of // the metadata necessary to pick up a trace and continue. Instead, we just re-import enough // information to be able to run a simulation off the trace store. pub fn import(data: Vec, maybe_duration: &Option) -> anyhow::Result { let mut exported_trace = rmp_serde::from_slice::(&data).map_err(TraceStoreError::ParseFailed)?; - if exported_trace.version != CURRENT_TRACE_VERSION { + if exported_trace.version != CURRENT_TRACE_FORMAT_VERSION { bail!("unsupported trace version: {}", exported_trace.version); } diff --git a/sk-store/src/tests/import_export_test.rs b/sk-store/src/tests/import_export_test.rs index ffea3294..5d2d1a29 100644 --- a/sk-store/src/tests/import_export_test.rs +++ b/sk-store/src/tests/import_export_test.rs @@ -7,12 +7,10 @@ use clockabilly::mock::MockUtcClock; use futures::stream; use futures::stream::StreamExt; use k8s_openapi::apimachinery::pkg::apis::meta::v1 as metav1; -use kube::api::DynamicObject; use kube::runtime::watcher::Event; use kube::ResourceExt; use sk_api::v1::ExportFilters; use sk_core::macros::*; -use sk_core::prelude::*; use super::*; use crate::watchers::{ diff --git a/sk-store/src/tests/trace_store_test.rs b/sk-store/src/tests/trace_store_test.rs index 36eaf43f..a838d91f 100644 --- a/sk-store/src/tests/trace_store_test.rs +++ b/sk-store/src/tests/trace_store_test.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use assertables::*; -use kube::api::DynamicObject; use sk_api::v1::ExportFilters; use sk_core::k8s::GVK; diff --git a/sk-store/src/watchers/dyn_obj_watcher.rs b/sk-store/src/watchers/dyn_obj_watcher.rs index 33fd5236..8ae4cd84 100644 --- a/sk-store/src/watchers/dyn_obj_watcher.rs +++ b/sk-store/src/watchers/dyn_obj_watcher.rs @@ -8,7 +8,6 @@ use futures::{ StreamExt, TryStreamExt, }; -use kube::api::DynamicObject; use kube::runtime::watcher::watcher; use kube::runtime::WatchStreamExt; use sk_core::errors::*; @@ -17,6 +16,7 @@ use sk_core::k8s::{ ApiSet, GVK, }; +use sk_core::prelude::*; use crate::watchers::{ EventHandler, diff --git a/sk-store/src/watchers/tests/mod.rs b/sk-store/src/watchers/tests/mod.rs index 30f5aaf4..b9d28169 100644 --- a/sk-store/src/watchers/tests/mod.rs +++ b/sk-store/src/watchers/tests/mod.rs @@ -1,7 +1,6 @@ mod pod_watcher_test; use futures::stream; -use kube::api::DynamicObject; use mockall::predicate; use rstest::*; use sk_core::prelude::*;