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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ bazel-*
Thumbs.db
._*

# Rust
target/

# Logs
*.log
3 changes: 3 additions & 0 deletions kq/loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum LoadingPhase {
ConvertingPods,
ConvertingNamespaces,
ConvertingDaemonSets,
ConvertingDeployments,
Finalizing,
}

Expand All @@ -45,6 +46,7 @@ impl LoadingPhase {
LoadingPhase::ConvertingPods => "Converting pods",
LoadingPhase::ConvertingNamespaces => "Converting namespaces",
LoadingPhase::ConvertingDaemonSets => "Converting daemonsets",
LoadingPhase::ConvertingDeployments => "Converting deployments",
LoadingPhase::Finalizing => "Finalizing",
}
}
Expand Down Expand Up @@ -492,6 +494,7 @@ impl SnapshotLoader {
pods: None,
namespaces: None,
daemon_sets: None,
deployments: None,
},
tables: final_tables,
table_batches,
Expand Down
5 changes: 3 additions & 2 deletions kq/loader/progress_reporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,11 @@ impl ProgressReporter for TerminalProgressReporter {
let icon = match phase {
LoadingPhase::ReadingFile => "📖",
LoadingPhase::ParsingJSON => "🔍",
LoadingPhase::ConvertingNodes |
LoadingPhase::ConvertingNodes |
LoadingPhase::ConvertingPods |
LoadingPhase::ConvertingNamespaces |
LoadingPhase::ConvertingDaemonSets => "⚡",
LoadingPhase::ConvertingDaemonSets |
LoadingPhase::ConvertingDeployments => "⚡",
LoadingPhase::Finalizing => "✨",
};
bar.set_message(format!("{} {}", icon, message));
Expand Down
8 changes: 8 additions & 0 deletions kq/loader/resource_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ pub(crate) const RESOURCE_TABLES: &[ResourceTable] = &[
parquet_file: "daemonsets.parquet",
schema: NestedSchemas::daemon_sets_nested_schema,
},
ResourceTable {
table_name: "deployments",
json_key: "deployments",
ndjson_file: "deployments.ndjson.gz",
ipc_file: "deployments.arrow",
parquet_file: "deployments.parquet",
schema: NestedSchemas::deployments_nested_schema,
},
];

pub(crate) fn resource_for_json_key(json_key: &str) -> Option<ResourceTable> {
Expand Down
5 changes: 4 additions & 1 deletion kq/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ impl QueryEngine {
"nodes" => "Kubernetes nodes with capacity, allocatable resources, and conditions".to_string(),
"namespaces" => "Kubernetes namespaces with metadata and status".to_string(),
"daemon_sets" => "Kubernetes DaemonSets with replica status information".to_string(),
"deployments" => "Kubernetes Deployments with replica status information".to_string(),
_ => format!("Kubernetes resource table: {}", table_name),
}
}
Expand Down Expand Up @@ -573,6 +574,7 @@ mod tests {
namespaces: Some(vec![]),
daemon_sets: Some(vec![]),
pods: Some(vec![]),
deployments: Some(vec![]),
},
tables: HashMap::new(),
table_batches: HashMap::new(),
Expand Down Expand Up @@ -673,6 +675,7 @@ mod tests {
"status": { "phase": "Active" }
}],
"daemonSets": [],
"deployments": [],
"pods": pods
});

Expand Down Expand Up @@ -937,7 +940,7 @@ mod tests {
.iter()
.map(|table| table.name.clone())
.collect();
for expected in ["pods", "nodes", "namespaces", "daemon_sets"] {
for expected in ["pods", "nodes", "namespaces", "daemon_sets", "deployments"] {
assert!(
names.contains(expected),
"missing user-facing view '{expected}'; got {names:?}"
Expand Down
1 change: 1 addition & 0 deletions kq/schema/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ rust_test(
"//kq/tests:golden_node.json",
"//kq/tests:golden_namespace.json",
"//kq/tests:golden_daemonset.json",
"//kq/tests:golden_deployment.json",
],
deps = [
"@crates//:arrow-json",
Expand Down
21 changes: 20 additions & 1 deletion kq/schema/kubernetes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::{
// LabelSelectorRequirement,
};

// Apps v1 - Deployment
pub use k8s_openapi::api::apps::v1::{
Deployment,
DeploymentSpec,
DeploymentStatus,
DeploymentCondition,
DeploymentStrategy,
};

// Note: ResourceList is now a BTreeMap<String, Quantity>
// We'll keep a type alias for compatibility with our converters
pub use k8s_openapi::apimachinery::pkg::api::resource::Quantity;
Expand All @@ -97,6 +106,8 @@ pub struct ClusterSnapshot {
#[serde(rename = "daemonSets", default)]
pub daemon_sets: Option<Vec<DaemonSet>>,
pub pods: Option<Vec<Pod>>,
#[serde(default)]
pub deployments: Option<Vec<Deployment>>,
}

impl ClusterSnapshot {
Expand All @@ -107,12 +118,14 @@ impl ClusterSnapshot {
let namespace_count = self.namespaces.as_ref().map_or(0, |n| n.len());
let daemonset_count = self.daemon_sets.as_ref().map_or(0, |d| d.len());
let pod_count = self.pods.as_ref().map(|p| p.len());
let deployment_count = self.deployments.as_ref().map_or(0, |d| d.len());
SnapshotSummary {
timestamp: ts,
node_count,
namespace_count,
daemonset_count,
pod_count,
deployment_count,
}
}
}
Expand All @@ -124,6 +137,7 @@ pub struct SnapshotSummary {
pub namespace_count: usize,
pub daemonset_count: usize,
pub pod_count: Option<usize>,
pub deployment_count: usize,
}

#[cfg(test)]
Expand All @@ -138,7 +152,8 @@ mod tests {
"nodes": [],
"namespaces": [],
"daemonSets": [],
"pods": []
"pods": [],
"deployments": []
}
"#;

Expand All @@ -147,6 +162,7 @@ mod tests {
assert_eq!(snapshot.namespaces.as_ref().map_or(0, |n| n.len()), 0);
assert_eq!(snapshot.daemon_sets.as_ref().map_or(0, |d| d.len()), 0);
assert!(snapshot.pods.is_some());
assert_eq!(snapshot.deployments.as_ref().map_or(0, |d| d.len()), 0);
}

#[test]
Expand All @@ -157,11 +173,13 @@ mod tests {
namespaces: Some(vec![]),
daemon_sets: Some(vec![]),
pods: Some(vec![]),
deployments: Some(vec![]),
};

let summary = snapshot.get_summary();
assert_eq!(summary.node_count, 0);
assert_eq!(summary.pod_count, Some(0));
assert_eq!(summary.deployment_count, 0);
}

#[test]
Expand All @@ -171,6 +189,7 @@ mod tests {
let _pod: Option<Pod> = None;
let _namespace: Option<Namespace> = None;
let _daemonset: Option<DaemonSet> = None;
let _deployment: Option<Deployment> = None;

// This test ensures the types compile
assert!(true);
Expand Down
27 changes: 26 additions & 1 deletion kq/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ impl SchemaRegistry {
self.schemas.insert("nodes".to_string(), Self::nodes_schema());
self.schemas.insert("namespaces".to_string(), Self::namespaces_schema());
self.schemas.insert("daemon_sets".to_string(), Self::daemon_sets_schema());
self.schemas.insert("deployments".to_string(), Self::deployments_schema());
}

pub fn get_schema(&self, table_name: &str) -> Option<Arc<Schema>> {
Expand Down Expand Up @@ -209,6 +210,28 @@ impl SchemaRegistry {
Arc::new(Schema::new(fields))
}

/// Schema for deployments table
pub fn deployments_schema() -> Arc<Schema> {
let fields = vec![
// All fields nullable for robustness
Field::new("name", DataType::Utf8, true),
Field::new("namespace", DataType::Utf8, true),
Field::new("uid", DataType::Utf8, true),
Field::new("creation_timestamp", DataType::Timestamp(TimeUnit::Millisecond, None), true),
Field::new("labels", DataType::Utf8, true), // JSON string for now
Field::new("replicas", DataType::Int32, true),
Field::new("ready_replicas", DataType::Int32, true),
Field::new("available_replicas", DataType::Int32, true),
Field::new("updated_replicas", DataType::Int32, true),
Field::new("unavailable_replicas", DataType::Int32, true),

// Hidden JSON column - contains full object JSON
Field::new(".json", DataType::Utf8, true),
];

Arc::new(Schema::new(fields))
}

}

impl Default for SchemaRegistry {
Expand All @@ -226,11 +249,12 @@ mod tests {
let registry = SchemaRegistry::new();
let tables = registry.list_tables();

assert_eq!(tables.len(), 4);
assert_eq!(tables.len(), 5);
assert!(tables.contains(&"pods".to_string()));
assert!(tables.contains(&"nodes".to_string()));
assert!(tables.contains(&"namespaces".to_string()));
assert!(tables.contains(&"daemon_sets".to_string()));
assert!(tables.contains(&"deployments".to_string()));
}

#[test]
Expand Down Expand Up @@ -283,6 +307,7 @@ mod tests {
assert!(tables.contains(&"nodes".to_string()));
assert!(tables.contains(&"namespaces".to_string()));
assert!(tables.contains(&"daemon_sets".to_string()));
assert!(tables.contains(&"deployments".to_string()));
}

#[test]
Expand Down
Loading