feat: Pods and container explorer for Kube protocol (#281)

This commit is contained in:
Christian Visintin
2024-10-02 12:24:46 +02:00
committed by GitHub
parent c5f76ec51c
commit ae1638ee17
27 changed files with 217 additions and 252 deletions

View File

@@ -193,8 +193,6 @@ impl AuthActivity {
}
fn load_bookmark_kube_into_gui(&mut self, params: KubeProtocolParams) {
self.mount_kube_pod_name(params.pod.as_str());
self.mount_kube_container(&params.container);
self.mount_kube_cluster_url(params.cluster_url.as_deref().unwrap_or(""));
self.mount_kube_namespace(params.namespace.as_deref().unwrap_or(""));
self.mount_kube_client_cert(params.client_cert.as_deref().unwrap_or(""));

View File

@@ -832,40 +832,6 @@ impl Component<Msg, NoUserEvent> for InputWebDAVUri {
// kube
#[derive(MockComponent)]
pub struct InputKubePodName {
component: Input,
}
impl InputKubePodName {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder("pod-name", Style::default().fg(Color::Rgb(128, 128, 128)))
.title("Pod name", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubePodName {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubePodNameBlurDown),
Msg::Ui(UiMsg::KubePodNameBlurUp),
)
}
}
#[derive(MockComponent)]
pub struct InputKubeNamespace {
component: Input,
@@ -937,40 +903,6 @@ impl Component<Msg, NoUserEvent> for InputKubeClusterUrl {
}
}
#[derive(MockComponent)]
pub struct InputKubeContainer {
component: Input,
}
impl InputKubeContainer {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder("container", Style::default().fg(Color::Rgb(128, 128, 128)))
.title("Kube container", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubeContainer {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubeContainerBlurDown),
Msg::Ui(UiMsg::KubeContainerBlurUp),
)
}
}
#[derive(MockComponent)]
pub struct InputKubeUsername {
component: Input,

View File

@@ -16,12 +16,11 @@ pub use bookmarks::{
#[cfg(unix)]
pub use form::InputSmbWorkgroup;
pub use form::{
InputAddress, InputKubeClientCert, InputKubeClientKey, InputKubeClusterUrl, InputKubeContainer,
InputKubeNamespace, InputKubePodName, InputKubeUsername, InputLocalDirectory, InputPassword,
InputPort, InputRemoteDirectory, InputS3AccessKey, InputS3Bucket, InputS3Endpoint,
InputS3Profile, InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken,
InputS3SessionToken, InputSmbShare, InputUsername, InputWebDAVUri, ProtocolRadio,
RadioS3NewPathStyle,
InputAddress, InputKubeClientCert, InputKubeClientKey, InputKubeClusterUrl, InputKubeNamespace,
InputKubeUsername, InputLocalDirectory, InputPassword, InputPort, InputRemoteDirectory,
InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3Profile, InputS3Region,
InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken, InputSmbShare,
InputUsername, InputWebDAVUri, ProtocolRadio, RadioS3NewPathStyle,
};
pub use popup::{
ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup,

View File

@@ -85,9 +85,7 @@ impl AuthActivity {
/// Get input values from fields or return an error if fields are invalid to work as aws s3
pub(super) fn collect_kube_host_params(&self) -> Result<FileTransferParams, &'static str> {
let params = self.get_kube_params_input();
if params.pod.is_empty() {
return Err("Invalid pod name");
}
Ok(FileTransferParams {
protocol: FileTransferProtocol::Kube,
params: ProtocolParams::Kube(params),

View File

@@ -48,8 +48,6 @@ pub enum Id {
InfoPopup,
InstallUpdatePopup,
Keybindings,
KubePodName,
KubeContainer,
KubeNamespace,
KubeClusterUrl,
KubeUsername,
@@ -119,10 +117,6 @@ pub enum UiMsg {
CloseKeybindingsPopup,
CloseQuitPopup,
CloseSaveBookmark,
KubePodNameBlurDown,
KubePodNameBlurUp,
KubeContainerBlurDown,
KubeContainerBlurUp,
KubeNamespaceBlurDown,
KubeNamespaceBlurUp,
KubeClusterUrlBlurDown,

View File

@@ -70,7 +70,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Password,
InputMask::Smb => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName,
InputMask::Kube => &Id::KubeNamespace,
InputMask::WebDAV => &Id::Password,
})
.is_ok());
@@ -84,7 +84,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Password,
InputMask::Smb => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName,
InputMask::Kube => &Id::KubeNamespace,
InputMask::WebDAV => &Id::Password,
})
.is_ok());
@@ -210,7 +210,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Address,
InputMask::Smb => &Id::Address,
InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName,
InputMask::Kube => &Id::KubeNamespace,
InputMask::WebDAV => &Id::WebDAVUri,
})
.is_ok());
@@ -305,23 +305,11 @@ impl AuthActivity {
UiMsg::KubeClientKeyBlurUp => {
assert!(self.app.active(&Id::KubeClientCert).is_ok());
}
UiMsg::KubeContainerBlurDown => {
assert!(self.app.active(&Id::KubeNamespace).is_ok());
}
UiMsg::KubeContainerBlurUp => {
assert!(self.app.active(&Id::KubePodName).is_ok());
}
UiMsg::KubePodNameBlurDown => {
assert!(self.app.active(&Id::KubeContainer).is_ok());
}
UiMsg::KubePodNameBlurUp => {
assert!(self.app.active(&Id::Protocol).is_ok());
}
UiMsg::KubeNamespaceBlurDown => {
assert!(self.app.active(&Id::KubeClusterUrl).is_ok());
}
UiMsg::KubeNamespaceBlurUp => {
assert!(self.app.active(&Id::KubeContainer).is_ok());
assert!(self.app.active(&Id::Protocol).is_ok());
}
UiMsg::KubeClusterUrlBlurDown => {
assert!(self.app.active(&Id::KubeUsername).is_ok());

View File

@@ -64,9 +64,7 @@ impl AuthActivity {
self.mount_kube_client_cert("");
self.mount_kube_client_key("");
self.mount_kube_cluster_url("");
self.mount_kube_container("");
self.mount_kube_namespace("");
self.mount_kube_pod_name("");
self.mount_kube_username("");
self.mount_smb_share("");
#[cfg(unix)]
@@ -748,30 +746,6 @@ impl AuthActivity {
.is_ok());
}
pub(super) fn mount_kube_pod_name(&mut self, value: &str) {
let color = self.theme().auth_address;
assert!(self
.app
.remount(
Id::KubePodName,
Box::new(components::InputKubePodName::new(value, color)),
vec![]
)
.is_ok());
}
pub(super) fn mount_kube_container(&mut self, value: &str) {
let color = self.theme().auth_password;
assert!(self
.app
.remount(
Id::KubeContainer,
Box::new(components::InputKubeContainer::new(value, color)),
vec![]
)
.is_ok());
}
pub(super) fn mount_kube_namespace(&mut self, value: &str) {
let color = self.theme().auth_port;
assert!(self
@@ -906,16 +880,12 @@ impl AuthActivity {
/// Collect s3 input values from view
pub(super) fn get_kube_params_input(&self) -> KubeProtocolParams {
let pod = self.get_input_kube_pod_name();
let container = self.get_input_kube_container();
let namespace = self.get_input_kube_namespace();
let cluster_url = self.get_input_kube_cluster_url();
let username = self.get_input_kube_username();
let client_cert = self.get_input_kube_client_cert();
let client_key = self.get_input_kube_client_key();
KubeProtocolParams {
pod,
container,
namespace,
cluster_url,
username,
@@ -1083,20 +1053,6 @@ impl AuthActivity {
)
}
pub(super) fn get_input_kube_pod_name(&self) -> String {
match self.app.state(&Id::KubePodName) {
Ok(State::One(StateValue::String(x))) => x,
_ => String::new(),
}
}
pub(super) fn get_input_kube_container(&self) -> String {
match self.app.state(&Id::KubeContainer) {
Ok(State::One(StateValue::String(x))) => x,
_ => String::new(),
}
}
pub(super) fn get_input_kube_namespace(&self) -> Option<String> {
match self.app.state(&Id::KubeNamespace) {
Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x),
@@ -1214,15 +1170,13 @@ impl AuthActivity {
}
ProtocolParams::Kube(params) => {
format!(
"{}://{}@{}{}{}",
"{}://{}{}",
protocol,
params.container,
params.pod,
params
.namespace
.as_deref()
.map(|x| format!("/{x}"))
.unwrap_or_default(),
.unwrap_or_else(|| String::from("default")),
params
.cluster_url
.as_deref()
@@ -1318,18 +1272,6 @@ impl AuthActivity {
/// Get the visible element in the kube form, based on current focus
fn get_kube_view(&self) -> [Id; 4] {
match self.app.focus() {
Some(&Id::KubePodName) => [
Id::KubePodName,
Id::KubeContainer,
Id::KubeNamespace,
Id::KubeClusterUrl,
],
Some(&Id::KubeUsername) => [
Id::KubeContainer,
Id::KubeNamespace,
Id::KubeClusterUrl,
Id::KubeUsername,
],
Some(&Id::KubeClientCert) => [
Id::KubeNamespace,
Id::KubeClusterUrl,
@@ -1355,10 +1297,10 @@ impl AuthActivity {
Id::LocalDirectory,
],
_ => [
Id::KubePodName,
Id::KubeContainer,
Id::KubeNamespace,
Id::KubeClusterUrl,
Id::KubeUsername,
Id::KubeClientCert,
],
}
}

View File

@@ -111,7 +111,9 @@ impl FileTransferActivity {
match &ft_params.params {
ProtocolParams::Generic(params) => params.address.clone(),
ProtocolParams::AwsS3(params) => params.bucket_name.clone(),
ProtocolParams::Kube(params) => params.pod.clone(),
ProtocolParams::Kube(params) => {
params.namespace.clone().unwrap_or("default".to_string())
}
ProtocolParams::Smb(params) => params.address.clone(),
ProtocolParams::WebDAV(params) => params.uri.clone(),
}
@@ -137,11 +139,9 @@ impl FileTransferActivity {
format!("Connecting to {}", params.bucket_name)
}
ProtocolParams::Kube(params) => {
info!(
"Client is not connected to remote; connecting to pod {}",
params.pod,
);
format!("Connecting to {}", params.pod)
let namespace = params.namespace.as_deref().unwrap_or("default");
info!("Client is not connected to remote; connecting to namespace {namespace}",);
format!("Connecting to Kube namespace {namespace}",)
}
ProtocolParams::Smb(params) => {
info!(

View File

@@ -57,7 +57,11 @@ impl FileTransferActivity {
// Connect to remote
match self.client.connect() {
Ok(Welcome { banner, .. }) => {
self.connected = true;
self.connected = self.client.is_connected();
if !self.connected {
return;
}
if let Some(banner) = banner {
// Log welcome
self.log(
@@ -68,6 +72,15 @@ impl FileTransferActivity {
banner
),
);
} else {
// Log welcome
self.log(
LogLevel::Info,
format!(
"Established connection with '{}'",
self.get_remote_hostname()
),
);
}
// Try to change directory to entry directory
let mut remote_chdir: Option<PathBuf> = None;
@@ -111,16 +124,28 @@ impl FileTransferActivity {
/// Reload remote directory entries and update browser
pub(super) fn reload_remote_dir(&mut self) {
if !self.connected {
return;
}
// Get current entries
if let Ok(wrkdir) = self.client.pwd() {
self.mount_blocking_wait("Loading remote directory...");
if self.remote_scan(wrkdir.as_path()).is_ok() {
// Set wrkdir
self.remote_mut().wrkdir = wrkdir;
}
let res = self.remote_scan(wrkdir.as_path());
self.umount_wait();
match res {
Ok(_) => {
self.remote_mut().wrkdir = wrkdir;
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not scan current remote directory: {err}"),
);
}
}
}
}
@@ -130,11 +155,21 @@ impl FileTransferActivity {
let wrkdir: PathBuf = self.host.pwd();
if self.local_scan(wrkdir.as_path()).is_ok() {
self.local_mut().wrkdir = wrkdir;
}
let res = self.local_scan(wrkdir.as_path());
self.umount_wait();
match res {
Ok(_) => {
self.local_mut().wrkdir = wrkdir;
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not scan current local directory: {err}"),
);
}
}
}
/// Scan current local directory
@@ -146,13 +181,7 @@ impl FileTransferActivity {
Ok(())
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not scan current directory: {err}"),
);
Err(err)
}
Err(err) => Err(err),
}
}
@@ -164,13 +193,7 @@ impl FileTransferActivity {
self.remote_mut().set_files(files);
Ok(())
}
Err(err) => {
self.log_and_alert(
LogLevel::Error,
format!("Could not scan current directory: {err}"),
);
Err(err)
}
Err(err) => Err(err),
}
}

View File

@@ -80,7 +80,7 @@ impl FileTransferActivity {
self.refresh_remote_status_bar();
// Update components
self.update_local_filelist();
self.update_remote_filelist();
// self.update_remote_filelist();
// Global listener
self.mount_global_listener();
// Give focus to local explorer