Skip to content

Commit

Permalink
infinyon#2427 allowing delete of multiple connectors, smart modules a…
Browse files Browse the repository at this point in the history
…nd topics. (infinyon#2518)

Solves infinyon#2427

Allows delete of multiple:
- connectors, 
- smart modules 
- topics

Left out the cluster delete. Does not seem suitable to be able to delete multiple clusters at once.

To be fixed:
- [x] continue-on-error instead of ignore-error
- [x] changelog entry
- [x] update on subcommand description
- [x] friendly error message with `CliError::from(error)`
- [x] split `CliError::print`
- [x] separate error type for multi delete fail


Co-authored-by: tomuxmon <tomas.sql@protonmail.com>
  • Loading branch information
tomuxmon and tomuxmon committed Jul 29, 2022
1 parent 5d176ff commit 365384d
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Platform Version 0.9.33 - UNRELEASED
* Added `DeliverySemantic` to `fluvio-cli`. ([#2508](https://github.com/infinyon/fluvio/pull/2508))
* CLI: Added ability to delete multiple connectors, smart modules and topics with one command. ([#2427](https://github.com/infinyon/fluvio/issues/2427))

## Platform Version 0.9.32 - 2022-07-26
* Restrict usage of `--initial`, `--extra-params` and `--join-topic` in `fluvio consume`. Those options only should be accepted when using specific smartmodules. ([#2476](https://github.com/infinyon/fluvio/pull/2476))
Expand Down
3 changes: 2 additions & 1 deletion crates/fluvio-cli/src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ fn main() -> Result<()> {

// If the CLI comes back with an error, attempt to handle it
if let Err(e) = run_block_on(root.process()) {
e.print()?;
let user_error = e.get_user_error()?;
eprintln!("{}", user_error);
std::process::exit(1);
}

Expand Down
40 changes: 33 additions & 7 deletions crates/fluvio-cli/src/connector/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,53 @@
//! CLI tree to generate Delete Managed Connectors
//!
use clap::Parser;

use fluvio::Fluvio;
use fluvio::metadata::connector::ManagedConnectorSpec;

use crate::CliError;
use tracing::debug;

// -----------------------------------
// CLI Options
// -----------------------------------

#[derive(Debug, Parser)]
pub struct DeleteManagedConnectorOpt {
/// The name of the connector to delete
#[clap(value_name = "name")]
name: String,
/// Continue deleting in case of an error
#[clap(short, long, action, required = false)]
continue_on_error: bool,
/// One or more name(s) of the connector(s) to be deleted
#[clap(value_name = "name", required = true)]
names: Vec<String>,
}

impl DeleteManagedConnectorOpt {
pub async fn process(self, fluvio: &Fluvio) -> Result<(), CliError> {
let admin = fluvio.admin().await;
admin.delete::<ManagedConnectorSpec, _>(&self.name).await?;
Ok(())
let mut err_happened = false;
for name in self.names.iter() {
debug!(name, "deleting connector");
if let Err(error) = admin.delete::<ManagedConnectorSpec, _>(name).await {
let error = CliError::from(error);
err_happened = true;
if self.continue_on_error {
let user_error = match error.get_user_error() {
Ok(usr_err) => usr_err.to_string(),
Err(err) => format!("{}", err),
};
println!("connector \"{}\" delete failed with: {}", name, user_error);
} else {
return Err(error);
}
} else {
println!("connector \"{}\" deleted", name);
}
}
if err_happened {
Err(CliError::CollectedError(
"Failed deleting connector(s). Check previous errors.".to_string(),
))
} else {
Ok(())
}
}
}
2 changes: 1 addition & 1 deletion crates/fluvio-cli/src/connector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub enum ManagedConnectorCmd {
)]
Update(UpdateManagedConnectorOpt),

/// Delete a Managed Connector
/// Delete one or more Managed Connectors with the given name(s)
#[clap(
name = "delete",
help_template = COMMAND_TEMPLATE,
Expand Down
36 changes: 14 additions & 22 deletions crates/fluvio-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ pub enum CliError {
InvalidArg(String),
#[error("Unknown error: {0}")]
Other(String),
#[error("{0}")]
CollectedError(String),
#[error("Unexpected Infallible error")]
Infallible(#[from] Infallible),
#[error("Dataplane error: {0}")]
Expand Down Expand Up @@ -117,57 +119,47 @@ impl CliError {
}
}

/// Looks at the error value and attempts to gracefully handle reporting it
/// Looks at the error value and attempts to create a user facing error message
///
/// Sometimes, specific errors require specific user-facing error messages.
/// Here is where we define those messages, as well as the exit code that the
/// program should return when exiting after those errors.
pub fn print(self) -> Result<()> {
pub fn get_user_error(self) -> Result<&'static str> {
match &self {
Self::ClientError(FluvioError::AdminApi(api)) => match api {
ApiError::Code(ErrorCode::TopicAlreadyExists, _) => {
println!("Topic already exists");
Ok(())
Ok("Topic already exists")
}
ApiError::Code(ErrorCode::ManagedConnectorAlreadyExists, _) => {
println!("Connector already exists");
Ok(())
}
ApiError::Code(ErrorCode::TopicNotFound, _) => {
println!("Topic not found");
Ok(())
Ok("Connector already exists")
}
ApiError::Code(ErrorCode::TopicNotFound, _) => Ok("Topic not found"),
ApiError::Code(ErrorCode::SmartModuleNotFound{ name: _ }, _) => Ok("Smart Module not found"),
ApiError::Code(ErrorCode::ManagedConnectorNotFound, _) => {
println!("Connector not found");
Ok(())
Ok("Connector not found")
}
ApiError::Code(ErrorCode::TopicInvalidName, _) => {
println!("Invalid topic name: topic name may only include lowercase letters (a-z), numbers (0-9), and hyphens (-).");
Ok(())
Ok("Invalid topic name: topic name may only include lowercase letters (a-z), numbers (0-9), and hyphens (-).")
}
ApiError::Code(ErrorCode::TableFormatAlreadyExists, _) => {
println!("TableFormat already exists");
Ok(())
Ok("TableFormat already exists")
}
ApiError::Code(ErrorCode::TableFormatNotFound, _) => {
println!("TableFormat not found");
Ok(())
Ok("TableFormat not found")
}
_ => Err(self),
},
Self::ClientError(FluvioError::Socket(SocketError::Io(io)))
if io.kind() == ErrorKind::TimedOut =>
{
println!("Network connection timed out while waiting for response");
Ok(())
Ok("Network connection timed out while waiting for response")
}
#[cfg(feature = "k8s")]
Self::ClusterCliError(ClusterCliError::TargetError(TargetError::ClientError(
FluvioError::Socket(SocketError::Io(io)),
))) => match io.kind() {
ErrorKind::ConnectionRefused => {
println!("Failed to connect to cluster, make sure you have started or connected to your cluster");
Ok(())
Ok("Failed to connect to cluster, make sure you have started or connected to your cluster")
}
_ => Err(self),
},
Expand Down
40 changes: 37 additions & 3 deletions crates/fluvio-cli/src/smartmodule/delete.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
use clap::Parser;
use crate::Result;
use crate::error::CliError;
use fluvio::Fluvio;
use fluvio::metadata::smartmodule::SmartModuleSpec;
use tracing::debug;

/// Delete an existing SmartModule with the given name
#[derive(Debug, Parser)]
pub struct DeleteSmartModuleOpt {
name: String,
/// Continue deleting in case of an error
#[clap(short, long, action, required = false)]
continue_on_error: bool,
/// One or more name(s) of the smart module(s) to be deleted
#[clap(value_name = "name", required = true)]
names: Vec<String>,
}

impl DeleteSmartModuleOpt {
pub async fn process(self, fluvio: &Fluvio) -> Result<()> {
let admin = fluvio.admin().await;
admin.delete::<SmartModuleSpec, _>(&self.name).await?;
Ok(())
let mut err_happened = false;
for name in self.names.iter() {
debug!(name, "deleting smart module");
if let Err(error) = admin.delete::<SmartModuleSpec, _>(name).await {
let error = CliError::from(error);
err_happened = true;
if self.continue_on_error {
let user_error = match error.get_user_error() {
Ok(usr_err) => usr_err.to_string(),
Err(err) => format!("{}", err),
};
println!(
"smart module \"{}\" delete failed with: {}",
name, user_error
);
} else {
return Err(error);
}
} else {
println!("smart module \"{}\" deleted", name);
}
}
if err_happened {
Err(CliError::CollectedError(
"Failed deleting smart module(s). Check previous errors.".to_string(),
))
} else {
Ok(())
}
}
}
1 change: 1 addition & 0 deletions crates/fluvio-cli/src/smartmodule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use self::delete::DeleteSmartModuleOpt;
pub enum SmartModuleCmd {
Create(CreateSmartModuleOpt),
List(ListSmartModuleOpt),
/// Delete one or more Smart Modules with the given name(s)
Delete(DeleteSmartModuleOpt),
}

Expand Down
41 changes: 33 additions & 8 deletions crates/fluvio-cli/src/topic/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,49 @@
use tracing::debug;
use clap::Parser;

use fluvio::Fluvio;
use fluvio::metadata::topic::TopicSpec;
use crate::Result;
use crate::error::CliError;

#[derive(Debug, Parser)]
pub struct DeleteTopicOpt {
/// The name of the Topic to delete
#[clap(value_name = "name")]
topic: String,
/// Continue deleting in case of an error
#[clap(short, long, action, required = false)]
continue_on_error: bool,
/// One or more name(s) of the topic(s) to be deleted
#[clap(value_name = "name", required = true)]
names: Vec<String>,
}

impl DeleteTopicOpt {
pub async fn process(self, fluvio: &Fluvio) -> Result<()> {
debug!("deleting topic: {}", &self.topic);
let admin = fluvio.admin().await;
admin.delete::<TopicSpec, _>(&self.topic).await?;
println!("topic \"{}\" deleted", &self.topic);
Ok(())
let mut err_happened = false;
for name in self.names.iter() {
debug!(name, "deleting topic");
if let Err(error) = admin.delete::<TopicSpec, _>(name).await {
let error = CliError::from(error);
err_happened = true;
if self.continue_on_error {
let user_error = match error.get_user_error() {
Ok(usr_err) => usr_err.to_string(),
Err(err) => format!("{}", err),
};
println!("topic \"{}\" delete failed with: {}", name, user_error);
} else {
return Err(error);
}
} else {
println!("topic \"{}\" deleted", name);
}
}
if err_happened {
Err(CliError::CollectedError(
"Failed deleting topic(s). Check previous errors.".to_string(),
))
} else {
Ok(())
}
}
}
2 changes: 1 addition & 1 deletion crates/fluvio-cli/src/topic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub enum TopicCmd {
)]
Create(CreateTopicOpt),

/// Delete a Topic with the given name
/// Delete one or more Topics with the given name(s)
#[clap(
name = "delete",
help_template = COMMAND_TEMPLATE,
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/smoke_tests/smart-module-basic.bats
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ setup_file() {
debug_msg "status: $status"
debug_msg "output: ${lines[3]}"
assert_failure
assert_output --partial "SmartModuleNotFound"
assert_output --partial "Smart Module not found"
}

0 comments on commit 365384d

Please sign in to comment.