mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
feat: Pods and container explorer for Kube protocol (#281)
This commit is contained in:
committed by
GitHub
parent
c5f76ec51c
commit
ae1638ee17
@@ -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(¶ms.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(""));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user