diff --git a/src/cluster.rs b/src/cluster.rs index 74c9ebb..387901f 100644 --- a/src/cluster.rs +++ b/src/cluster.rs @@ -9,21 +9,18 @@ use kube::{ }; use log::info; use serde::{Deserialize, Serialize}; -use serde_json::Value; use std::sync::Arc; use tokio::task::spawn; #[derive(Serialize, Deserialize, Default, Debug)] pub(crate) struct Cluster { - version: String, - deprecated_api_result: Vec, + deprecated_api_result: Vec, } impl<'a> Cluster { - pub(crate) async fn new(version: String) -> anyhow::Result { + pub(crate) async fn new(version: Vec) -> anyhow::Result { Ok(Cluster { - version: version.to_owned(), - deprecated_api_result: Self::get_deprecated_api(&version).await?, + deprecated_api_result: Self::get_deprecated_api(version).await?, }) } } @@ -37,7 +34,6 @@ impl Finder for Cluster { "Connected to cluster {:?}", current_config.current_context.unwrap() ); - info!("Target apiversions v{}", &self.version); let m = self.deprecated_api_result.to_owned(); let join_handle: ClusterOP = m .into_iter() @@ -46,10 +42,11 @@ impl Finder for Cluster { let client_clone = Arc::clone(&arc_client); spawn(async move { let mut temp_table: Vec = vec![]; - let kind = resource["kind"].as_str().unwrap().to_string(); - let group = resource["group"].as_str().unwrap().to_string(); - let version = resource["version"].as_str().unwrap().to_string(); - let removed = resource["removed"].as_str().unwrap().to_string(); + let kind = resource.kind.to_string(); + let group = resource.group.to_string(); + let version = resource.version.to_string(); + let removed = resource.removed.to_string(); + let k8_version = resource.k8_version.to_owned().unwrap().to_string(); let gvk = GroupVersionKind::gvk(&group, &version, &kind); let ar = if let Ok((ar, _)) = pinned_kind(&(*client_clone).clone(), &gvk).await { @@ -74,6 +71,7 @@ impl Finder for Cluster { name, supported_api_version: "REMOVED".to_string(), deprecated_api_version: "REMOVED".to_string(), + k8_version: k8_version.to_string(), }); } else { let annotations = item.annotations(); @@ -94,6 +92,7 @@ impl Finder for Cluster { name, supported_api_version: supported_version, deprecated_api_version: ls_app_ver, + k8_version: k8_version.to_string(), }); } } diff --git a/src/file.rs b/src/file.rs index aa94835..10e5e70 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,34 +1,31 @@ -use crate::utils::{Finder, TableDetails}; +use crate::utils::{Api, Finder, TableDetails}; use async_trait::async_trait; use jwalk::{Parallelism, WalkDir}; use rayon::iter::ParallelBridge; use rayon::prelude::ParallelIterator; use serde::{Deserialize, Serialize}; -use serde_json::Value; use std::fs::File; use std::io::Read; use std::path::Path; use std::sync::mpsc::{channel, Sender}; use yaml_rust::{Yaml, YamlLoader}; -type SenderChannel = Sender<(String, String, String, String, String)>; +type SenderChannel = Sender<(String, String, String, String, String, String)>; #[derive(Serialize, Deserialize, Default, Debug)] pub(crate) struct FileSystem { - version: String, file_dir: String, - deprecated_apis: Vec, + deprecated_apis: Vec, } impl<'a> FileSystem { - pub(crate) async fn new(file_dir: String, version: String) -> anyhow::Result { + pub(crate) async fn new(file_dir: String, version: Vec) -> anyhow::Result { Ok(FileSystem { file_dir, - version: version.to_owned(), - deprecated_apis: Self::get_deprecated_api(&version).await?, + deprecated_apis: Self::get_deprecated_api(version).await?, }) } - fn found_deprecated_api( + fn find_deprecated_api( &self, doc: Yaml, path: &Path, @@ -36,20 +33,12 @@ impl<'a> FileSystem { ) -> anyhow::Result<()> { if let Some(mut api_version) = doc["apiVersion"].as_str() { for z in self.deprecated_apis.iter() { - if z["kind"] - .as_str() - .unwrap() - .eq(doc["kind"].as_str().unwrap()) - { - let mut supported_api_version = format!( - "{}/{}", - z["group"].as_str().unwrap(), - z["version"].as_str().unwrap() - ); + if z.kind.eq(doc["kind"].as_str().unwrap()) { + let mut supported_api_version = format!("{}/{}", z.group, z.version); let p = path.file_name().unwrap().to_str().unwrap().to_string(); let mut send = false; - if z["removed"].as_str().unwrap().eq("true") { + if z.removed.eq("true") { supported_api_version = "REMOVED".to_string(); api_version = "REMOVED"; send = true @@ -61,6 +50,7 @@ impl<'a> FileSystem { api_version.to_string(), doc["metadata"]["name"].as_str().unwrap().to_string(), p, + z.k8_version.as_ref().unwrap().to_string(), ))? } } @@ -78,37 +68,35 @@ impl<'a> Finder for FileSystem { .parallelism(Parallelism::RayonNewPool(0)) .into_iter() .par_bridge() - .try_for_each_with( - sender, - |sed: &mut Sender<(String, String, String, String, String)>, op| { - let dir_entry = op.ok().unwrap(); - if dir_entry.file_type().is_file() { - let path = dir_entry.path(); - if let Some(yaml_file) = path.extension() { - if yaml_file.eq("yaml") { - let mut file = File::open(&path).expect("Unable to open file"); - let mut contents = String::new(); - file.read_to_string(&mut contents) - .expect("Unable to read file"); - let docs = YamlLoader::load_from_str(&contents)?; - for doc in docs { - Self::found_deprecated_api(self, doc, &path, sed)?; - } + .try_for_each_with(sender, |sed: &mut SenderChannel, op| { + let dir_entry = op.ok().unwrap(); + if dir_entry.file_type().is_file() { + let path = dir_entry.path(); + if let Some(yaml_file) = path.extension() { + if yaml_file.eq("yaml") { + let mut file = File::open(&path).expect("Unable to open file"); + let mut contents = String::new(); + file.read_to_string(&mut contents) + .expect("Unable to read file"); + let docs = YamlLoader::load_from_str(&contents)?; + for doc in docs { + Self::find_deprecated_api(self, doc, &path, sed)?; } } } - Ok(()) - }, - ); + } + Ok(()) + }); let res: Vec<_> = receiver.iter().collect(); let mut temp_table: Vec = vec![]; - for (kind, supported_api_version, deprecated_api_version, name, path) in res { + for (kind, supported_api_version, deprecated_api_version, name, path, k8_version) in res { temp_table.push(TableDetails { kind, namespace: path, name, supported_api_version, deprecated_api_version, + k8_version, }); } Ok(temp_table) diff --git a/src/main.rs b/src/main.rs index d350346..0166a3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ +use std::process::exit; + use file::FileSystem; +use log::error; use utils::{Finder, Output, Scrape}; mod cluster; mod file; @@ -7,6 +10,7 @@ use crate::cluster::Cluster; use crate::utils::init_logger; use clap::Parser; +const K8_VERSIONS: [&str; 4] = ["1.16", "1.22", "1.25", "1.26"]; #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Sunset { @@ -36,12 +40,17 @@ impl Sunset { #[tokio::main] async fn main() -> anyhow::Result<()> { + init_logger(); let cli = Sunset::parse(); - // You can check the value provided by positional arguments, or option arguments - let version: String = if let Some(version) = &cli.target_version { - version.to_string() + let versions: Vec = if let Some(version) = &cli.target_version { + if K8_VERSIONS.contains(&version.as_str()) { + [version.to_string()].to_vec() + } else { + error!("Version {} does not have any deprecated apis", version); + exit(0); + } } else { - "1.16".to_string() + K8_VERSIONS.iter().map(|v| v.to_string()).collect() }; match cli.debug { @@ -50,40 +59,42 @@ async fn main() -> anyhow::Result<()> { } _ => std::env::set_var("RUST_LOG", "info,kube=info"), } - - init_logger(); - match cli.check_scrape_type() { Scrape::Cluster(col_replace) => { - let c = Cluster::new(version).await?; + let c = Cluster::new(versions).await?; let x = utils::VecTableDetails(c.find_deprecated_api().await?); - match cli.output { - Output::Csv => { - x.generate_csv(col_replace)?; - } - Output::Junit => { - println!("Junit"); - } - Output::Table => { - x.generate_table(col_replace)?; + if !x.0.is_empty() { + match cli.output { + Output::Csv => { + x.generate_csv(col_replace)?; + } + Output::Junit => { + println!("Junit"); + } + Output::Table => { + x.generate_table(col_replace)?; + } } } } Scrape::Dir(loc, col_replace) => { - let c = FileSystem::new(loc, version).await?; + let c = FileSystem::new(loc, versions).await?; let x = utils::VecTableDetails(c.find_deprecated_api().await?); - match cli.output { - Output::Csv => { - x.generate_csv(col_replace)?; - } - Output::Junit => { - println!("Junit"); - } - Output::Table => { - x.generate_table(col_replace)?; + if !x.0.is_empty() { + match cli.output { + Output::Csv => { + x.generate_csv(col_replace)?; + } + Output::Junit => { + println!("Junit"); + } + Output::Table => { + x.generate_table(col_replace)?; + } } } } }; + Ok(()) } diff --git a/src/utils.rs b/src/utils.rs index c77cdc6..a78cd6c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,11 +3,11 @@ use clap::ArgEnum; use comfy_table::{ContentArrangement, Table}; use csv::Writer; use env_logger::{Builder, Env}; -use log::info; +use log::{debug, info}; use async_trait::async_trait; + use serde::{Deserialize, Serialize}; -use serde_json::Value; use std::{fs::File, io::Write}; use tokio::task::JoinHandle; @@ -28,6 +28,7 @@ pub(crate) struct TableDetails { pub(crate) name: String, pub(crate) deprecated_api_version: String, pub(crate) supported_api_version: String, + pub(crate) k8_version: String, } impl VecTableDetails { @@ -41,6 +42,7 @@ impl VecTableDetails { r.name, r.deprecated_api_version, r.supported_api_version, + r.k8_version, ]); } println!("{t}"); @@ -56,6 +58,7 @@ impl VecTableDetails { r.name, r.deprecated_api_version, r.supported_api_version, + r.k8_version, ])?; } wtr.flush()?; @@ -74,6 +77,7 @@ pub(crate) fn generate_table_header<'a>(t: &'a mut Table, column_replace: &str) "Name", "DeprecatedApiVersion", "SupportedApiVersion", + "K8sVersion", ]) .set_content_arrangement(ContentArrangement::Dynamic) } @@ -85,6 +89,7 @@ pub(crate) fn generate_csv_header(wtr: &mut Writer, column_replace: &str) "Name", "DeprecatedApiVersion", "SupportedApiVersion", + "K8sVersion", ])?; Ok(()) } @@ -95,6 +100,9 @@ pub(crate) struct Deprecated { } pub(crate) fn init_logger() { + if std::env::var("RUST_LOG").is_err() { + std::env::set_var("RUST_LOG", "info"); + } let env = Env::default() .filter("RUST_LOG") .write_style("MY_LOG_STYLE"); @@ -124,15 +132,37 @@ pub(crate) enum Scrape<'a> { Dir(String, &'a str), } +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub(crate) struct Api { + pub(crate) kind: String, + pub(crate) group: String, + pub(crate) version: String, + pub(crate) removed: String, + pub(crate) k8_version: Option, +} + #[async_trait] pub(crate) trait Finder { async fn find_deprecated_api(&self) -> Result>; - async fn get_deprecated_api(version: &String) -> anyhow::Result> { - let url = format!( + async fn get_deprecated_api(versions: Vec) -> anyhow::Result> { + //let mut apis: Vec = vec![]; + let mut output: Vec = vec![]; + for version in versions { + info!( + "Getting list of deperecated apis in kubernetes version {}", + version + ); + let url = format!( "https://raw.githubusercontent.com/maheshrayas/k8s_deprecated_api/main/v{}/data.json", version ); - let v: Value = reqwest::get(url).await?.json().await?; - Ok(v.as_array().unwrap().to_owned()) + debug!("deprecated list url {}", url); + let v: Vec = reqwest::get(url).await?.json().await?; + for mut k in v { + k.k8_version = Some(version.to_owned()); + output.push(k) + } + } + Ok(output) } }