diff --git a/src/delete.rs b/src/delete.rs index d529365..ece5632 100644 --- a/src/delete.rs +++ b/src/delete.rs @@ -4,7 +4,7 @@ use clap::{Args, Subcommand}; use itertools::Itertools; use crate::config::Config; use crate::refresh::refreshed_state; -use crate::skate::ConfigFileArgs; +use crate::skate::{ConfigFileArgs, ResourceType}; use crate::ssh; #[derive(Debug, Args)] @@ -58,27 +58,32 @@ async fn delete_ingress(args: DeleteResourceArgs) -> Result<(), Box> let state = refreshed_state(&config.current_context.clone().unwrap_or("".to_string()), &conns, &config).await?; + let mut results = vec!(); - // let result = conns.execute("skatelet", &["delete", "ingress", &args.name, "--namespace", &args.namespace]); - // for (node, output) in result { - // match output { - // Ok(output) => { - // if output.exit_status == 0 { - // println!("deleted ingress {} on {}", args.name, node.name); - // } else { - // eprintln!("failed to delete ingress {} on {}: {}", args.name, node.name, output.stderr); - // } - // } - // Err(e) => { - // eprintln!("failed to delete ingress {} on {}: {}", args.name, node.name, e); - // } - // } - // } + for conn in conns.clients { + let result = conn.remove_resource(ResourceType::Ingress, &args.name, &args.namespace).await; + results.push(result); + } + + for result in &results { + match result { + Ok(_) => { + println!("deleted resource") + } + Err(e) => { + eprintln!("{}", e) + } + } + } // delete resources from nodes they're on using skatelet // update state - Ok(()) + if results.is_empty() { + return Ok(()); + } + + Err(anyhow!("failed to delete resource").into()) } async fn delete_node(args: DeleteResourceArgs) -> Result<(), Box> { diff --git a/src/executor.rs b/src/executor.rs index 73425d8..3219227 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -13,7 +13,7 @@ use crate::util::{hash_string, metadata_name}; pub trait Executor { fn apply(&self, manifest: &str) -> Result<(), Box>; - fn remove(&self, manifest: &str, grace: Option) -> Result<(), Box>; + fn manifest_delete(&self, object: SupportedResources, grace: Option) -> Result<(), Box>; } pub struct DefaultExecutor { @@ -196,8 +196,9 @@ impl Executor for DefaultExecutor { } } - fn remove(&self, manifest: &str, grace_period: Option) -> Result<(), Box> { - let object: SupportedResources = serde_yaml::from_str(manifest).expect("failed to deserialize manifest"); + + + fn manifest_delete(&self, object: SupportedResources, grace_period: Option) -> Result<(), Box> { let namespaced_name = object.name(); match object { diff --git a/src/scheduler.rs b/src/scheduler.rs index ba2a144..818165f 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -358,7 +358,7 @@ impl DefaultScheduler { let conn = conns.find(&resource.node.unwrap().node_name).ok_or("failed to find connection to host")?; let manifest = serde_yaml::to_string(&resource.resource).expect("failed to serialize manifest"); - match conn.remove_resource(&manifest).await { + match conn.remove_resource_by_manifest(&manifest).await { Ok(_) => Ok(()), Err(err) => Err(err) } diff --git a/src/skatelet/apply.rs b/src/skatelet/apply.rs index 8bc0720..38dae42 100644 --- a/src/skatelet/apply.rs +++ b/src/skatelet/apply.rs @@ -7,6 +7,12 @@ use std::io::{Read}; use crate::executor::{DefaultExecutor, Executor}; +use crate::skate::SupportedResources; +use crate::skate::SupportedResources::Ingress; + +use k8s_openapi::api::networking::v1::Ingress as K8sIngress; +use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta; +use log::Metadata; #[derive(Debug, Args)] @@ -23,7 +29,7 @@ pub struct ApplyArgs { } -#[derive(Debug, Subcommand)] +#[derive(Debug, Subcommand, Clone)] pub enum StdinCommand { #[command(name = "-", about = "feed manifest yaml via stdin")] Stdin {}, @@ -43,24 +49,72 @@ pub fn apply(apply_args: ApplyArgs) -> Result<(), Box> { executor.apply(&manifest) } -#[derive(Debug, Args)] -pub struct RemoveArgs { +#[derive(Debug, Args, Clone)] +pub struct DeleteResourceArgs { + #[arg(long, long_help = "Name of the resource.")] + pub name: String, + #[arg(long, long_help = "Name of the resource.")] + pub namespace: String, +} + +#[derive(Debug, Subcommand, Clone)] +pub enum DeleteResourceCommands { + #[command(flatten)] + StdinCommand(StdinCommand), + Ingress(DeleteResourceArgs), +} + +#[derive(Debug, Args, Clone)] +pub struct DeleteArgs { #[arg(short, long, long_help("Number of seconds to wait before hard killing."))] termination_grace_period: Option, #[command(subcommand)] - command: StdinCommand, + command: DeleteResourceCommands, } -pub fn remove(args: RemoveArgs) -> Result<(), Box> { - let manifest = match args.command { - StdinCommand::Stdin {} => { - let mut stdin = io::stdin(); - let mut buffer = String::new(); - stdin.read_to_string(&mut buffer)?; - buffer - } +pub fn delete(args: DeleteArgs) -> Result<(), Box> { + match &args.command { + DeleteResourceCommands::Ingress(resource_args) => delete_ingress(args.clone(), resource_args.clone()), + DeleteResourceCommands::StdinCommand(_) => delete_stdin(args), + } +} + + +pub fn delete_ingress(delete_args: DeleteArgs, resource_args: DeleteResourceArgs) -> Result<(), Box> { + let executor = DefaultExecutor::new(); + + executor.manifest_delete(Ingress(K8sIngress { + metadata: ObjectMeta { + annotations: None, + creation_timestamp: None, + deletion_grace_period_seconds: None, + deletion_timestamp: None, + finalizers: None, + generate_name: None, + generation: None, + labels: None, + managed_fields: None, + name: Some(resource_args.name), + namespace: Some(resource_args.namespace), + owner_references: None, + resource_version: None, + self_link: None, + uid: None, + }, + spec: None, + status: None, + }), delete_args.termination_grace_period) +} + +pub fn delete_stdin(args: DeleteArgs) -> Result<(), Box> { + let manifest = { + let mut stdin = io::stdin(); + let mut buffer = String::new(); + stdin.read_to_string(&mut buffer)?; + buffer }; let executor = DefaultExecutor::new(); - executor.remove(&manifest, args.termination_grace_period) + let object: SupportedResources = serde_yaml::from_str(&manifest).expect("failed to deserialize manifest"); + executor.manifest_delete(object, args.termination_grace_period) } diff --git a/src/skatelet/skatelet.rs b/src/skatelet/skatelet.rs index 00c6ba3..f05afa8 100644 --- a/src/skatelet/skatelet.rs +++ b/src/skatelet/skatelet.rs @@ -1,7 +1,7 @@ use std::error::Error; use clap::{Parser, Subcommand}; use crate::skatelet::apply; -use crate::skatelet::apply::{ApplyArgs, remove, RemoveArgs}; +use crate::skatelet::apply::{ApplyArgs, delete, DeleteArgs}; use crate::skatelet::cni::cni; use crate::skatelet::system::{system, SystemArgs}; use crate::skatelet::template::{template, TemplateArgs}; @@ -20,7 +20,7 @@ struct Cli { enum Commands { Apply(ApplyArgs), System(SystemArgs), - Remove(RemoveArgs), + Delete(DeleteArgs), Template(TemplateArgs), Cni, } @@ -30,7 +30,7 @@ pub async fn skatelet() -> Result<(), Box> { match args.command { Commands::Apply(args) => apply::apply(args), Commands::System(args) => system(args).await, - Commands::Remove(args) => remove(args), + Commands::Delete(args) => delete(args), Commands::Template(args) => template(args), Commands::Cni => { cni(); diff --git a/src/ssh.rs b/src/ssh.rs index 3b1f122..e009143 100644 --- a/src/ssh.rs +++ b/src/ssh.rs @@ -11,7 +11,7 @@ use cni_plugin::Command; use futures::stream::FuturesUnordered; use itertools::{Either, Itertools}; use crate::config::{Cluster, Node}; -use crate::skate::{Distribution, Os, Platform}; +use crate::skate::{Distribution, Os, Platform, ResourceType}; use futures::StreamExt; use serde::{Deserialize, Serialize}; use crate::skatelet::SystemInfo; @@ -211,9 +211,25 @@ echo ovs=$(cat /tmp/ovs-$$); } } - pub async fn remove_resource(&self, manifest: &str) -> Result<(String, String), Box> { + pub async fn remove_resource(&self, resource_type: ResourceType, name: &str, namespace: &str) -> Result<(String, String), Box> { + let result = self.client.execute(&format!("sudo skatelet delete {} --name {} --namespace {}", resource_type, name, namespace)).await?; + match result.exit_status { + 0 => { + Ok((result.stdout, result.stderr)) + } + _ => { + let message = match result.stderr.len() { + 0 => result.stdout, + _ => result.stderr + }; + Err(anyhow!("failed to remove resource: exit code {}, {}", result.exit_status, message).into()) + } + } + } + + pub async fn remove_resource_by_manifest(&self, manifest: &str) -> Result<(String, String), Box> { let base64_manifest = general_purpose::STANDARD.encode(manifest); - let result = self.client.execute(&format!("echo \"{}\" |base64 --decode|sudo skatelet remove -", base64_manifest)).await?; + let result = self.client.execute(&format!("echo \"{}\" |base64 --decode|sudo skatelet delete -", base64_manifest)).await?; match result.exit_status { 0 => { Ok((result.stdout, result.stderr)) @@ -342,8 +358,7 @@ impl SshClients { self.clients.iter().find(|c| c.node_name == node_name) } pub async fn execute(&self, command: &str, args: &[&str]) -> Vec<(String, Result>)> { - - let concat_command = &format!("{} {}", &command, args.join(" ")); + let concat_command = &format!("{} {}", &command, args.join(" ")); let fut: FuturesUnordered<_> = self.clients.iter().map(|c| { c.execute(concat_command) }).collect();