Skip to content

Commit

Permalink
feat: show validation results in xray
Browse files Browse the repository at this point in the history
  • Loading branch information
drmorr0 committed Oct 30, 2024
1 parent 83967b2 commit c8bfda2
Show file tree
Hide file tree
Showing 19 changed files with 325 additions and 161 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ either = "1.12.0"
futures = "0.3.28"
json-patch = "1.2.0"
k8s-openapi = { version = "0.19.0", features = ["v1_27"] }
lazy_static = "1.5.0"
object_store = { version = "0.11.0", features = ["aws", "gcp", "azure", "http"] }
# remove this fork once https://github.com/uutils/parse_datetime/pull/80 is merged and a new version released
parse_datetime_fork = { version = "0.6.0-custom" }
Expand Down
1 change: 1 addition & 0 deletions sk-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dirs = { workspace = true }
json-patch = { workspace = true }
kube = { workspace = true }
k8s-openapi = { workspace = true }
lazy_static = { workspace = true }
ratatui = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
Expand Down
41 changes: 25 additions & 16 deletions sk-cli/src/validation/annotated_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ use sk_store::{
TraceStore,
};

use super::validator::Validator;
use super::validator::{
Validator,
ValidatorCode,
};

#[derive(Clone, Default)]
pub struct AnnotatedTraceEvent {
pub data: TraceEvent,
pub annotations: Vec<(usize, String)>,
pub annotations: Vec<Vec<ValidatorCode>>,
}

impl AnnotatedTraceEvent {
pub fn new(data: TraceEvent) -> AnnotatedTraceEvent {
let len = data.applied_objs.len() + data.deleted_objs.len();
let annotations = vec![vec![]; len];

AnnotatedTraceEvent { data, annotations }
}
}

#[derive(Default)]
Expand All @@ -29,18 +41,15 @@ pub struct AnnotatedTrace {
base: TraceStore,
path: String,
events: Vec<AnnotatedTraceEvent>,
summary: BTreeMap<String, usize>,
summary: BTreeMap<ValidatorCode, usize>,
}

impl AnnotatedTrace {
pub async fn new(trace_path: &str) -> anyhow::Result<AnnotatedTrace> {
let object_store = SkObjectStore::new(trace_path)?;
let trace_data = object_store.get().await?.to_vec();
let base = TraceStore::import(trace_data, &None)?;
let events = base
.iter()
.map(|(event, _)| AnnotatedTraceEvent { data: event.clone(), ..Default::default() })
.collect();
let events = base.iter().map(|(event, _)| AnnotatedTraceEvent::new(event.clone())).collect();
Ok(AnnotatedTrace {
base,
events,
Expand All @@ -49,16 +58,16 @@ impl AnnotatedTrace {
})
}

pub fn validate(&mut self, validators: &mut BTreeMap<String, Validator>) {
for evt in self.events.iter_mut() {
pub fn validate(&mut self, validators: &mut BTreeMap<ValidatorCode, Validator>) {
for event in self.events.iter_mut() {
for (code, validator) in validators.iter_mut() {
let mut affected_indices: Vec<_> =
validator.check_next_event(evt).into_iter().map(|i| (i, code.clone())).collect();
let affected_indices = validator.check_next_event(event);
let count = affected_indices.len();
self.summary.entry(code.into()).and_modify(|e| *e += count).or_insert(count);
self.summary.entry(*code).and_modify(|e| *e += count).or_insert(count);

// This needs to happen at the ends, since `append` consumes affected_indices' contents
evt.annotations.append(&mut affected_indices);
for i in affected_indices {
event.annotations[i].push(*code);
}
}
}
}
Expand All @@ -79,7 +88,7 @@ impl AnnotatedTrace {
self.events.first().map(|evt| evt.data.ts)
}

pub fn summary_iter(&self) -> btree_map::Iter<'_, String, usize> {
pub fn summary_iter(&self) -> btree_map::Iter<'_, ValidatorCode, usize> {
self.summary.iter()
}
}
Expand All @@ -98,7 +107,7 @@ impl AnnotatedTrace {
AnnotatedTrace { events, ..Default::default() }
}

pub fn summary_for(&self, code: &str) -> Option<usize> {
pub fn summary_for(&self, code: &ValidatorCode) -> Option<usize> {
self.summary.get(code).cloned()
}
}
13 changes: 10 additions & 3 deletions sk-cli/src/validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ use clap::{
};
use sk_core::prelude::*;

pub use self::annotated_trace::AnnotatedTrace;
pub use self::annotated_trace::{
AnnotatedTrace,
AnnotatedTraceEvent,
};
pub use self::validation_store::ValidationStore;
pub use self::validator::{
ValidatorCode,
ValidatorType,
};

#[derive(Subcommand)]
pub enum ValidateSubcommand {
Expand All @@ -33,8 +40,8 @@ pub struct CheckArgs {

#[derive(clap::Args)]
pub struct ExplainArgs {
#[arg(long_help = "Error code to explain")]
pub code: String,
#[arg(long_help = "Error code to explain", value_parser = ValidatorCode::parse)]
pub code: ValidatorCode,
}

#[derive(Clone, ValueEnum)]
Expand Down
85 changes: 57 additions & 28 deletions sk-cli/src/validation/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,72 @@
mod status_field_populated_test;
mod validation_store_test;

use std::collections::BTreeMap;

use rstest::*;
use sk_core::k8s::testutils::test_deployment;
use sk_store::TraceEvent;

use super::annotated_trace::AnnotatedTraceEvent;
use super::validator::{
Diagnostic,
Validator,
ValidatorCode,
ValidatorType,
};
use super::*;

const TEST_VALIDATOR_CODE: ValidatorCode = ValidatorCode(ValidatorType::Error, 9999);

#[fixture]
pub fn annotated_trace() -> AnnotatedTrace {
AnnotatedTrace::new_with_events(vec![
AnnotatedTraceEvent {
data: TraceEvent { ts: 0, ..Default::default() },
..Default::default()
},
AnnotatedTraceEvent {
data: TraceEvent {
ts: 1,
applied_objs: vec![test_deployment("test_depl1")],
deleted_objs: vec![],
},
..Default::default()
},
AnnotatedTraceEvent {
data: TraceEvent {
ts: 2,
applied_objs: vec![test_deployment("test_depl1"), test_deployment("test_depl2")],
deleted_objs: vec![],
},
..Default::default()
},
AnnotatedTraceEvent {
data: TraceEvent {
ts: 3,
applied_objs: vec![],
deleted_objs: vec![test_deployment("test_depl1")],
},
..Default::default()
},
AnnotatedTraceEvent::new(TraceEvent { ts: 0, ..Default::default() }),
AnnotatedTraceEvent::new(TraceEvent {
ts: 1,
applied_objs: vec![test_deployment("test_depl1")],
deleted_objs: vec![],
}),
AnnotatedTraceEvent::new(TraceEvent {
ts: 2,
applied_objs: vec![test_deployment("test_depl1"), test_deployment("test_depl2")],
deleted_objs: vec![],
}),
AnnotatedTraceEvent::new(TraceEvent {
ts: 3,
applied_objs: vec![],
deleted_objs: vec![test_deployment("test_depl1")],
}),
])
}

struct TestDiagnostic {}

impl Diagnostic for TestDiagnostic {
fn check_next_event(&mut self, evt: &mut AnnotatedTraceEvent) -> Vec<usize> {
if evt.data.applied_objs.len() > 1 {
vec![1]
} else {
vec![]
}
}

fn reset(&mut self) {}
}

#[fixture]
fn test_validator() -> Validator {
Validator {
type_: ValidatorType::Warning,
name: "test_validator",
help: "HELP ME, I'M STUCK IN THE BORROW CHECKER",
diagnostic: Box::new(TestDiagnostic {}),
}
}

#[fixture]
pub fn test_validation_store(test_validator: Validator) -> ValidationStore {
let mut test_store = ValidationStore { validators: BTreeMap::new() };
test_store.register_with_code(TEST_VALIDATOR_CODE, test_validator);
test_store
}
47 changes: 7 additions & 40 deletions sk-cli/src/validation/tests/validation_store_test.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,20 @@
use std::collections::BTreeMap;

use assertables::*;

use super::validator::{
Diagnostic,
Validator,
ValidatorType,
};
use super::*;

struct TestDiagnostic {}

impl Diagnostic for TestDiagnostic {
fn check_next_event(&mut self, evt: &mut AnnotatedTraceEvent) -> Vec<usize> {
if evt.data.applied_objs.len() > 1 {
vec![1]
} else {
vec![]
}
}

fn reset(&mut self) {}
}

#[fixture]
fn validator() -> Validator {
Validator {
type_: ValidatorType::Warning,
name: "test_validator",
help: "HELP ME, I'M STUCK IN THE BORROW CHECKER",
diagnostic: Box::new(TestDiagnostic {}),
}
}

#[rstest]
fn test_validate_trace(validator: Validator, mut annotated_trace: AnnotatedTrace) {
let code = "W9999";
let mut test_store = ValidationStore { validators: BTreeMap::new() };
test_store.register_with_code(code.into(), validator);

test_store.validate_trace(&mut annotated_trace);
fn test_validate_trace(mut test_validation_store: ValidationStore, mut annotated_trace: AnnotatedTrace) {
test_validation_store.validate_trace(&mut annotated_trace);

for evt in annotated_trace.iter() {
if evt.data.applied_objs.len() > 1 {
assert_eq!(evt.annotations, vec![(1, code.into())]);
assert_eq!(evt.annotations[1], vec![TEST_VALIDATOR_CODE]);
} else {
assert_is_empty!(evt.annotations);
for annotation in &evt.annotations {
assert_is_empty!(annotation);
}
}
}

assert_eq!(annotated_trace.summary_for(code).unwrap(), 1);
assert_eq!(annotated_trace.summary_for(&TEST_VALIDATOR_CODE).unwrap(), 1);
}
15 changes: 9 additions & 6 deletions sk-cli/src/validation/validation_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ use serde::Serialize;
use sk_core::prelude::*;

use super::annotated_trace::AnnotatedTrace;
use super::validator::Validator;
use super::validator::{
Validator,
ValidatorCode,
};
use super::{
status_field_populated,
PrintFormat,
};

#[derive(Serialize)]
pub struct ValidationStore {
pub(super) validators: BTreeMap<String, Validator>,
pub(super) validators: BTreeMap<ValidatorCode, Validator>,
}

impl ValidationStore {
Expand All @@ -25,15 +28,15 @@ impl ValidationStore {
trace.validate(&mut self.validators);
}

pub(super) fn explain(&self, code: &str) -> EmptyResult {
pub(super) fn explain(&self, code: &ValidatorCode) -> EmptyResult {
let v = self.lookup(code)?;
println!("{} ({code})", v.name);
println!("{:=<80}", "");
println!("{}", self.lookup(code)?.help);
Ok(())
}

pub(super) fn lookup<'a>(&'a self, code: &str) -> anyhow::Result<&'a Validator> {
pub(super) fn lookup<'a>(&'a self, code: &ValidatorCode) -> anyhow::Result<&'a Validator> {
self.validators.get(code).ok_or(anyhow!("code not found: {code}"))
}

Expand All @@ -49,11 +52,11 @@ impl ValidationStore {
}

pub(super) fn register(&mut self, v: Validator) {
let code = format!("{}{:04}", v.type_, self.validators.len());
let code = ValidatorCode(v.type_, self.validators.len());
self.register_with_code(code, v);
}

pub(super) fn register_with_code(&mut self, code: String, v: Validator) {
pub(super) fn register_with_code(&mut self, code: ValidatorCode, v: Validator) {
self.validators.insert(code, v);
}

Expand Down
Loading

0 comments on commit c8bfda2

Please sign in to comment.