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
+4
View File
@@ -41,6 +41,10 @@
Released on Released on
- [Issue 268](https://github.com/veeso/termscp/issues/268): Pods and container explorer for Kube protocol.
- BREAKING ‼️ Kube address argument has changed; see manual!
- Pod and container argumets have been removed; from now on you will connect with the following syntax to the provided namespace: `/pod-name/container-name/path/to/file`
## 0.14.0 ## 0.14.0
Released on 17/07/2024 Released on 17/07/2024
Generated
+83
View File
@@ -568,6 +568,19 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "dbus-secret-service"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1caa0c241c01ad8d99a78d553567d38f873dd3ac16eca33a5370d650ab25584e"
dependencies = [
"dbus",
"futures-util",
"num",
"once_cell",
"rand",
]
[[package]] [[package]]
name = "debug-helper" name = "debug-helper"
version = "0.3.13" version = "0.3.13"
@@ -1450,6 +1463,12 @@ name = "keyring"
version = "3.3.0" version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fa83d1ca02db069b5fbe94b23b584d588e989218310c9c15015bb5571ef1a94" checksum = "5fa83d1ca02db069b5fbe94b23b584d588e989218310c9c15015bb5571ef1a94"
dependencies = [
"byteorder",
"dbus-secret-service",
"security-framework",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "kqueue" name = "kqueue"
@@ -1809,12 +1828,76 @@ dependencies = [
"tauri-winrt-notification", "tauri-winrt-notification",
] ]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
+1 -1
View File
@@ -46,7 +46,7 @@ dirs = "^5.0"
edit = "^0.1" edit = "^0.1"
filetime = "^0.2" filetime = "^0.2"
hostname = "^0.4" hostname = "^0.4"
keyring = { version = "^3", optional = true } keyring = { version = "^3", optional = true, features = ["apple-native", "windows-native", "sync-secret-service"] }
lazy-regex = "^3" lazy-regex = "^3"
lazy_static = "^1" lazy_static = "^1"
log = "^0.4" log = "^0.4"
+1 -1
View File
@@ -133,7 +133,7 @@ s3://buckethead@eu-central-1:default:/assets
Falls Sie eine Verbindung zu Kube herstellen möchten, verwenden Sie die folgende Syntax Falls Sie eine Verbindung zu Kube herstellen möchten, verwenden Sie die folgende Syntax
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### SMB Adressargument #### SMB Adressargument
+1 -1
View File
@@ -110,7 +110,7 @@ s3://buckethead@eu-central-1:default:/assets
En caso de que quieras conectarte a Kube, utiliza la siguiente sintaxis En caso de que quieras conectarte a Kube, utiliza la siguiente sintaxis
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### Argumento de dirección de WebDAV #### Argumento de dirección de WebDAV
+1 -1
View File
@@ -108,7 +108,7 @@ s3://buckethead@eu-central-1:default:/assets
Si vous souhaitez vous connecter à Kube, utilisez la syntaxe suivante Si vous souhaitez vous connecter à Kube, utilisez la syntaxe suivante
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### Argument d'adresse WebDAV #### Argument d'adresse WebDAV
+1 -1
View File
@@ -106,7 +106,7 @@ s3://buckethead@eu-central-1:default:/assets
Nel caso tu voglia connetterti a Kube usa la seguente sintassi Nel caso tu voglia connetterti a Kube usa la seguente sintassi
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### Argomento indirizzo per WebDAV #### Argomento indirizzo per WebDAV
+1 -1
View File
@@ -111,7 +111,7 @@ s3://buckethead@eu-central-1:default:/assets
In case you want to connect to Kube use the following syntax In case you want to connect to Kube use the following syntax
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### WebDAV address argument #### WebDAV address argument
+1 -1
View File
@@ -111,7 +111,7 @@ s3://buckethead@eu-central-1:default:/assets
Caso queira se conectar ao Kube, use a seguinte sintaxe Caso queira se conectar ao Kube, use a seguinte sintaxe
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### Argumento de Endereço do WebDAV #### Argumento de Endereço do WebDAV
+1 -1
View File
@@ -108,7 +108,7 @@ s3://buckethead@eu-central-1:default:/assets
如果您想连接到 Kube,请使用以下语法 如果您想连接到 Kube,请使用以下语法
```txt ```txt
kube://<container>@<pod></path> kube://[namespace][@<cluster_url>][$</path>]
``` ```
#### WebDAV 地址参数 #### WebDAV 地址参数
-7
View File
@@ -351,8 +351,6 @@ mod tests {
#[test] #[test]
fn bookmark_from_kube_ftparams() { fn bookmark_from_kube_ftparams() {
let params = ProtocolParams::Kube(KubeProtocolParams { let params = ProtocolParams::Kube(KubeProtocolParams {
pod: "pod".to_string(),
container: "container".to_string(),
namespace: Some("default".to_string()), namespace: Some("default".to_string()),
username: Some("root".to_string()), username: Some("root".to_string()),
cluster_url: Some("https://localhost:6443".to_string()), cluster_url: Some("https://localhost:6443".to_string()),
@@ -368,8 +366,6 @@ mod tests {
assert!(bookmark.username.is_none()); assert!(bookmark.username.is_none());
assert!(bookmark.password.is_none()); assert!(bookmark.password.is_none());
let kube: &KubeParams = bookmark.kube.as_ref().unwrap(); let kube: &KubeParams = bookmark.kube.as_ref().unwrap();
assert_eq!(kube.pod_name.as_str(), "pod");
assert_eq!(kube.container.as_str(), "container");
assert_eq!(kube.namespace.as_deref().unwrap(), "default"); assert_eq!(kube.namespace.as_deref().unwrap(), "default");
assert_eq!( assert_eq!(
kube.cluster_url.as_deref().unwrap(), kube.cluster_url.as_deref().unwrap(),
@@ -494,8 +490,6 @@ mod tests {
remote_path: Some(PathBuf::from("/tmp")), remote_path: Some(PathBuf::from("/tmp")),
local_path: Some(PathBuf::from("/usr")), local_path: Some(PathBuf::from("/usr")),
kube: Some(KubeParams { kube: Some(KubeParams {
pod_name: String::from("pod"),
container: String::from("container"),
namespace: Some(String::from("default")), namespace: Some(String::from("default")),
cluster_url: Some(String::from("https://localhost:6443")), cluster_url: Some(String::from("https://localhost:6443")),
username: Some(String::from("root")), username: Some(String::from("root")),
@@ -516,7 +510,6 @@ mod tests {
std::path::Path::new("/usr") std::path::Path::new("/usr")
); );
let gparams = params.params.kube_params().unwrap(); let gparams = params.params.kube_params().unwrap();
assert_eq!(gparams.pod.as_str(), "pod");
assert_eq!(gparams.namespace.as_deref().unwrap(), "default"); assert_eq!(gparams.namespace.as_deref().unwrap(), "default");
assert_eq!( assert_eq!(
gparams.cluster_url.as_deref().unwrap(), gparams.cluster_url.as_deref().unwrap(),
-6
View File
@@ -5,8 +5,6 @@ use crate::filetransfer::params::KubeProtocolParams;
/// Extra Connection parameters for Kube protocol /// Extra Connection parameters for Kube protocol
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Default)] #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Default)]
pub struct KubeParams { pub struct KubeParams {
pub pod_name: String,
pub container: String,
pub namespace: Option<String>, pub namespace: Option<String>,
pub cluster_url: Option<String>, pub cluster_url: Option<String>,
pub username: Option<String>, pub username: Option<String>,
@@ -17,8 +15,6 @@ pub struct KubeParams {
impl From<KubeParams> for KubeProtocolParams { impl From<KubeParams> for KubeProtocolParams {
fn from(value: KubeParams) -> Self { fn from(value: KubeParams) -> Self {
Self { Self {
pod: value.pod_name,
container: value.container,
namespace: value.namespace, namespace: value.namespace,
cluster_url: value.cluster_url, cluster_url: value.cluster_url,
username: value.username, username: value.username,
@@ -31,8 +27,6 @@ impl From<KubeParams> for KubeProtocolParams {
impl From<KubeProtocolParams> for KubeParams { impl From<KubeProtocolParams> for KubeParams {
fn from(value: KubeProtocolParams) -> Self { fn from(value: KubeProtocolParams) -> Self {
Self { Self {
pod_name: value.pod,
container: value.container,
namespace: value.namespace, namespace: value.namespace,
cluster_url: value.cluster_url, cluster_url: value.cluster_url,
username: value.username, username: value.username,
+39 -6
View File
@@ -412,8 +412,6 @@ mod tests {
assert_eq!(host.password, None); assert_eq!(host.password, None);
assert_eq!(host.protocol, FileTransferProtocol::Kube); assert_eq!(host.protocol, FileTransferProtocol::Kube);
let kube = host.kube.as_ref().unwrap(); let kube = host.kube.as_ref().unwrap();
assert_eq!(kube.pod_name.as_str(), "my-pod");
assert_eq!(kube.container.as_str(), "my-container");
assert_eq!(kube.namespace.as_deref().unwrap(), "my-namespace"); assert_eq!(kube.namespace.as_deref().unwrap(), "my-namespace");
assert_eq!(kube.cluster_url.as_deref().unwrap(), "https://my-cluster"); assert_eq!(kube.cluster_url.as_deref().unwrap(), "https://my-cluster");
assert_eq!(kube.username.as_deref().unwrap(), "my-username"); assert_eq!(kube.username.as_deref().unwrap(), "my-username");
@@ -515,8 +513,6 @@ mod tests {
s3: None, s3: None,
smb: None, smb: None,
kube: Some(KubeParams { kube: Some(KubeParams {
pod_name: "my-pod".to_string(),
container: "my-container".to_string(),
namespace: Some("my-namespace".to_string()), namespace: Some("my-namespace".to_string()),
cluster_url: Some("https://my-cluster".to_string()), cluster_url: Some("https://my-cluster".to_string()),
username: Some("my-username".to_string()), username: Some("my-username".to_string()),
@@ -593,6 +589,22 @@ mod tests {
assert!(deserialize::<Theme>(Box::new(toml_file)).is_err()); assert!(deserialize::<Theme>(Box::new(toml_file)).is_err());
} }
#[test]
fn test_should_deserialize_v14_pod_bookmark() {
let toml = create_v14_pod_bookmark();
toml.as_file().sync_all().unwrap();
toml.as_file().rewind().unwrap();
let deserialized: UserHosts = deserialize(Box::new(toml)).unwrap();
let kube = deserialized.bookmarks.get("pod").unwrap();
assert_eq!(kube.protocol, FileTransferProtocol::Kube);
let kube = kube.kube.as_ref().unwrap();
assert_eq!(kube.namespace.as_deref().unwrap(), "my-namespace");
assert_eq!(kube.cluster_url.as_deref().unwrap(), "https://my-cluster");
assert_eq!(kube.username.as_deref().unwrap(), "my-username");
assert_eq!(kube.client_cert.as_deref().unwrap(), "my-cert");
assert_eq!(kube.client_key.as_deref().unwrap(), "my-key");
}
fn create_good_toml_bookmarks() -> tempfile::NamedTempFile { fn create_good_toml_bookmarks() -> tempfile::NamedTempFile {
// Write // Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap(); let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
@@ -617,8 +629,6 @@ mod tests {
[bookmarks.pod] [bookmarks.pod]
protocol = "KUBE" protocol = "KUBE"
[bookmarks.pod.kube] [bookmarks.pod.kube]
pod_name = "my-pod"
container = "my-container"
namespace = "my-namespace" namespace = "my-namespace"
cluster_url = "https://my-cluster" cluster_url = "https://my-cluster"
username = "my-username" username = "my-username"
@@ -644,6 +654,29 @@ mod tests {
tmpfile tmpfile
} }
fn create_v14_pod_bookmark() -> tempfile::NamedTempFile {
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
let file_content: &str = r#"
[bookmarks]
[bookmarks.pod]
protocol = "KUBE"
[bookmarks.pod.kube]
pod_name = "my-pod"
container = "my-container"
namespace = "my-namespace"
cluster_url = "https://my-cluster"
username = "my-username"
client_cert = "my-cert"
client_key = "my-key"
[recents]
"#;
tmpfile.write_all(file_content.as_bytes()).unwrap();
//write!(tmpfile, "[bookmarks]\nraspberrypi2 = {{ address = \"192.168.1.31\", port = 22, protocol = \"SFTP\", username = \"root\" }}\nmsi-estrem = {{ address = \"192.168.1.30\", port = 22, protocol = \"SFTP\", username = \"cvisintin\" }}\naws-server-prod1 = {{ address = \"51.23.67.12\", port = 21, protocol = \"FTPS\", username = \"aws001\" }}\n\n[recents]\nISO20201215T094000Z = {{ address = \"172.16.104.10\", port = 22, protocol = \"SCP\", username = \"root\" }}\n");
tmpfile
}
fn create_bad_toml_bookmarks() -> tempfile::NamedTempFile { fn create_bad_toml_bookmarks() -> tempfile::NamedTempFile {
// Write // Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap(); let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
+2 -4
View File
@@ -8,7 +8,7 @@ use std::sync::Arc;
use remotefs::RemoteFs; use remotefs::RemoteFs;
use remotefs_aws_s3::AwsS3Fs; use remotefs_aws_s3::AwsS3Fs;
use remotefs_ftp::FtpFs; use remotefs_ftp::FtpFs;
use remotefs_kube::KubeContainerFs as KubeFs; use remotefs_kube::KubeMultiPodFs as KubeFs;
#[cfg(smb_unix)] #[cfg(smb_unix)]
use remotefs_smb::SmbOptions; use remotefs_smb::SmbOptions;
#[cfg(smb)] #[cfg(smb)]
@@ -119,7 +119,7 @@ impl Builder {
.build() .build()
.expect("Unable to create tokio runtime"), .expect("Unable to create tokio runtime"),
); );
let kube_fs = KubeFs::new(&params.pod, &params.container, &rt); let kube_fs = KubeFs::new(&rt);
if let Some(config) = params.config() { if let Some(config) = params.config() {
kube_fs.config(config) kube_fs.config(config)
} else { } else {
@@ -281,8 +281,6 @@ mod test {
#[test] #[test]
fn test_should_build_kube_fs() { fn test_should_build_kube_fs() {
let params = ProtocolParams::Kube(KubeProtocolParams { let params = ProtocolParams::Kube(KubeProtocolParams {
pod: "pod".to_string(),
container: "container".to_string(),
namespace: Some("namespace".to_string()), namespace: Some("namespace".to_string()),
cluster_url: Some("cluster_url".to_string()), cluster_url: Some("cluster_url".to_string()),
username: Some("username".to_string()), username: Some("username".to_string()),
-2
View File
@@ -3,8 +3,6 @@ use remotefs_kube::Config;
/// Protocol params used by WebDAV /// Protocol params used by WebDAV
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct KubeProtocolParams { pub struct KubeProtocolParams {
pub pod: String,
pub container: String,
pub namespace: Option<String>, pub namespace: Option<String>,
pub cluster_url: Option<String>, pub cluster_url: Option<String>,
pub username: Option<String>, pub username: Option<String>,
-4
View File
@@ -76,16 +76,12 @@ impl KeyStorage for KeyringStorage {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(linux)]
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[cfg(linux)]
use whoami::username; use whoami::username;
#[cfg(linux)]
use super::*; use super::*;
#[test] #[test]
#[cfg(linux)]
fn test_system_keys_keyringstorage() { fn test_system_keys_keyringstorage() {
let username: String = username(); let username: String = username();
let storage: KeyringStorage = KeyringStorage::new(username.as_str()); let storage: KeyringStorage = KeyringStorage::new(username.as_str());
-2
View File
@@ -193,8 +193,6 @@ impl AuthActivity {
} }
fn load_bookmark_kube_into_gui(&mut self, params: KubeProtocolParams) { 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_cluster_url(params.cluster_url.as_deref().unwrap_or(""));
self.mount_kube_namespace(params.namespace.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("")); self.mount_kube_client_cert(params.client_cert.as_deref().unwrap_or(""));
-68
View File
@@ -832,40 +832,6 @@ impl Component<Msg, NoUserEvent> for InputWebDAVUri {
// kube // 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)] #[derive(MockComponent)]
pub struct InputKubeNamespace { pub struct InputKubeNamespace {
component: Input, 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)] #[derive(MockComponent)]
pub struct InputKubeUsername { pub struct InputKubeUsername {
component: Input, component: Input,
+5 -6
View File
@@ -16,12 +16,11 @@ pub use bookmarks::{
#[cfg(unix)] #[cfg(unix)]
pub use form::InputSmbWorkgroup; pub use form::InputSmbWorkgroup;
pub use form::{ pub use form::{
InputAddress, InputKubeClientCert, InputKubeClientKey, InputKubeClusterUrl, InputKubeContainer, InputAddress, InputKubeClientCert, InputKubeClientKey, InputKubeClusterUrl, InputKubeNamespace,
InputKubeNamespace, InputKubePodName, InputKubeUsername, InputLocalDirectory, InputPassword, InputKubeUsername, InputLocalDirectory, InputPassword, InputPort, InputRemoteDirectory,
InputPort, InputRemoteDirectory, InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3Profile, InputS3Region,
InputS3Profile, InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken, InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken, InputSmbShare,
InputS3SessionToken, InputSmbShare, InputUsername, InputWebDAVUri, ProtocolRadio, InputUsername, InputWebDAVUri, ProtocolRadio, RadioS3NewPathStyle,
RadioS3NewPathStyle,
}; };
pub use popup::{ pub use popup::{
ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup, ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup,
+1 -3
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 /// 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> { pub(super) fn collect_kube_host_params(&self) -> Result<FileTransferParams, &'static str> {
let params = self.get_kube_params_input(); let params = self.get_kube_params_input();
if params.pod.is_empty() {
return Err("Invalid pod name");
}
Ok(FileTransferParams { Ok(FileTransferParams {
protocol: FileTransferProtocol::Kube, protocol: FileTransferProtocol::Kube,
params: ProtocolParams::Kube(params), params: ProtocolParams::Kube(params),
-6
View File
@@ -48,8 +48,6 @@ pub enum Id {
InfoPopup, InfoPopup,
InstallUpdatePopup, InstallUpdatePopup,
Keybindings, Keybindings,
KubePodName,
KubeContainer,
KubeNamespace, KubeNamespace,
KubeClusterUrl, KubeClusterUrl,
KubeUsername, KubeUsername,
@@ -119,10 +117,6 @@ pub enum UiMsg {
CloseKeybindingsPopup, CloseKeybindingsPopup,
CloseQuitPopup, CloseQuitPopup,
CloseSaveBookmark, CloseSaveBookmark,
KubePodNameBlurDown,
KubePodNameBlurUp,
KubeContainerBlurDown,
KubeContainerBlurUp,
KubeNamespaceBlurDown, KubeNamespaceBlurDown,
KubeNamespaceBlurUp, KubeNamespaceBlurUp,
KubeClusterUrlBlurDown, KubeClusterUrlBlurDown,
+4 -16
View File
@@ -70,7 +70,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Password, InputMask::Generic => &Id::Password,
InputMask::Smb => &Id::Password, InputMask::Smb => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket, InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName, InputMask::Kube => &Id::KubeNamespace,
InputMask::WebDAV => &Id::Password, InputMask::WebDAV => &Id::Password,
}) })
.is_ok()); .is_ok());
@@ -84,7 +84,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Password, InputMask::Generic => &Id::Password,
InputMask::Smb => &Id::Password, InputMask::Smb => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket, InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName, InputMask::Kube => &Id::KubeNamespace,
InputMask::WebDAV => &Id::Password, InputMask::WebDAV => &Id::Password,
}) })
.is_ok()); .is_ok());
@@ -210,7 +210,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Address, InputMask::Generic => &Id::Address,
InputMask::Smb => &Id::Address, InputMask::Smb => &Id::Address,
InputMask::AwsS3 => &Id::S3Bucket, InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName, InputMask::Kube => &Id::KubeNamespace,
InputMask::WebDAV => &Id::WebDAVUri, InputMask::WebDAV => &Id::WebDAVUri,
}) })
.is_ok()); .is_ok());
@@ -305,23 +305,11 @@ impl AuthActivity {
UiMsg::KubeClientKeyBlurUp => { UiMsg::KubeClientKeyBlurUp => {
assert!(self.app.active(&Id::KubeClientCert).is_ok()); 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 => { UiMsg::KubeNamespaceBlurDown => {
assert!(self.app.active(&Id::KubeClusterUrl).is_ok()); assert!(self.app.active(&Id::KubeClusterUrl).is_ok());
} }
UiMsg::KubeNamespaceBlurUp => { UiMsg::KubeNamespaceBlurUp => {
assert!(self.app.active(&Id::KubeContainer).is_ok()); assert!(self.app.active(&Id::Protocol).is_ok());
} }
UiMsg::KubeClusterUrlBlurDown => { UiMsg::KubeClusterUrlBlurDown => {
assert!(self.app.active(&Id::KubeUsername).is_ok()); assert!(self.app.active(&Id::KubeUsername).is_ok());
+4 -62
View File
@@ -64,9 +64,7 @@ impl AuthActivity {
self.mount_kube_client_cert(""); self.mount_kube_client_cert("");
self.mount_kube_client_key(""); self.mount_kube_client_key("");
self.mount_kube_cluster_url(""); self.mount_kube_cluster_url("");
self.mount_kube_container("");
self.mount_kube_namespace(""); self.mount_kube_namespace("");
self.mount_kube_pod_name("");
self.mount_kube_username(""); self.mount_kube_username("");
self.mount_smb_share(""); self.mount_smb_share("");
#[cfg(unix)] #[cfg(unix)]
@@ -748,30 +746,6 @@ impl AuthActivity {
.is_ok()); .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) { pub(super) fn mount_kube_namespace(&mut self, value: &str) {
let color = self.theme().auth_port; let color = self.theme().auth_port;
assert!(self assert!(self
@@ -906,16 +880,12 @@ impl AuthActivity {
/// Collect s3 input values from view /// Collect s3 input values from view
pub(super) fn get_kube_params_input(&self) -> KubeProtocolParams { 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 namespace = self.get_input_kube_namespace();
let cluster_url = self.get_input_kube_cluster_url(); let cluster_url = self.get_input_kube_cluster_url();
let username = self.get_input_kube_username(); let username = self.get_input_kube_username();
let client_cert = self.get_input_kube_client_cert(); let client_cert = self.get_input_kube_client_cert();
let client_key = self.get_input_kube_client_key(); let client_key = self.get_input_kube_client_key();
KubeProtocolParams { KubeProtocolParams {
pod,
container,
namespace, namespace,
cluster_url, cluster_url,
username, 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> { pub(super) fn get_input_kube_namespace(&self) -> Option<String> {
match self.app.state(&Id::KubeNamespace) { match self.app.state(&Id::KubeNamespace) {
Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x), Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x),
@@ -1214,15 +1170,13 @@ impl AuthActivity {
} }
ProtocolParams::Kube(params) => { ProtocolParams::Kube(params) => {
format!( format!(
"{}://{}@{}{}{}", "{}://{}{}",
protocol, protocol,
params.container,
params.pod,
params params
.namespace .namespace
.as_deref() .as_deref()
.map(|x| format!("/{x}")) .map(|x| format!("/{x}"))
.unwrap_or_default(), .unwrap_or_else(|| String::from("default")),
params params
.cluster_url .cluster_url
.as_deref() .as_deref()
@@ -1318,18 +1272,6 @@ impl AuthActivity {
/// Get the visible element in the kube form, based on current focus /// Get the visible element in the kube form, based on current focus
fn get_kube_view(&self) -> [Id; 4] { fn get_kube_view(&self) -> [Id; 4] {
match self.app.focus() { 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) => [ Some(&Id::KubeClientCert) => [
Id::KubeNamespace, Id::KubeNamespace,
Id::KubeClusterUrl, Id::KubeClusterUrl,
@@ -1355,10 +1297,10 @@ impl AuthActivity {
Id::LocalDirectory, Id::LocalDirectory,
], ],
_ => [ _ => [
Id::KubePodName,
Id::KubeContainer,
Id::KubeNamespace, Id::KubeNamespace,
Id::KubeClusterUrl, Id::KubeClusterUrl,
Id::KubeUsername,
Id::KubeClientCert,
], ],
} }
} }
+6 -6
View File
@@ -111,7 +111,9 @@ impl FileTransferActivity {
match &ft_params.params { match &ft_params.params {
ProtocolParams::Generic(params) => params.address.clone(), ProtocolParams::Generic(params) => params.address.clone(),
ProtocolParams::AwsS3(params) => params.bucket_name.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::Smb(params) => params.address.clone(),
ProtocolParams::WebDAV(params) => params.uri.clone(), ProtocolParams::WebDAV(params) => params.uri.clone(),
} }
@@ -137,11 +139,9 @@ impl FileTransferActivity {
format!("Connecting to {}…", params.bucket_name) format!("Connecting to {}…", params.bucket_name)
} }
ProtocolParams::Kube(params) => { ProtocolParams::Kube(params) => {
info!( let namespace = params.namespace.as_deref().unwrap_or("default");
"Client is not connected to remote; connecting to pod {}", info!("Client is not connected to remote; connecting to namespace {namespace}",);
params.pod, format!("Connecting to Kube namespace {namespace}…",)
);
format!("Connecting to {}…", params.pod)
} }
ProtocolParams::Smb(params) => { ProtocolParams::Smb(params) => {
info!( info!(
+45 -22
View File
@@ -57,7 +57,11 @@ impl FileTransferActivity {
// Connect to remote // Connect to remote
match self.client.connect() { match self.client.connect() {
Ok(Welcome { banner, .. }) => { Ok(Welcome { banner, .. }) => {
self.connected = true; self.connected = self.client.is_connected();
if !self.connected {
return;
}
if let Some(banner) = banner { if let Some(banner) = banner {
// Log welcome // Log welcome
self.log( self.log(
@@ -68,6 +72,15 @@ impl FileTransferActivity {
banner banner
), ),
); );
} else {
// Log welcome
self.log(
LogLevel::Info,
format!(
"Established connection with '{}'",
self.get_remote_hostname()
),
);
} }
// Try to change directory to entry directory // Try to change directory to entry directory
let mut remote_chdir: Option<PathBuf> = None; let mut remote_chdir: Option<PathBuf> = None;
@@ -111,16 +124,28 @@ impl FileTransferActivity {
/// Reload remote directory entries and update browser /// Reload remote directory entries and update browser
pub(super) fn reload_remote_dir(&mut self) { pub(super) fn reload_remote_dir(&mut self) {
if !self.connected {
return;
}
// Get current entries // Get current entries
if let Ok(wrkdir) = self.client.pwd() { if let Ok(wrkdir) = self.client.pwd() {
self.mount_blocking_wait("Loading remote directory..."); self.mount_blocking_wait("Loading remote directory...");
if self.remote_scan(wrkdir.as_path()).is_ok() { let res = self.remote_scan(wrkdir.as_path());
// Set wrkdir
self.remote_mut().wrkdir = wrkdir;
}
self.umount_wait(); 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(); let wrkdir: PathBuf = self.host.pwd();
if self.local_scan(wrkdir.as_path()).is_ok() { let res = self.local_scan(wrkdir.as_path());
self.local_mut().wrkdir = wrkdir;
}
self.umount_wait(); 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 /// Scan current local directory
@@ -146,13 +181,7 @@ impl FileTransferActivity {
Ok(()) Ok(())
} }
Err(err) => { Err(err) => Err(err),
self.log_and_alert(
LogLevel::Error,
format!("Could not scan current directory: {err}"),
);
Err(err)
}
} }
} }
@@ -164,13 +193,7 @@ impl FileTransferActivity {
self.remote_mut().set_files(files); self.remote_mut().set_files(files);
Ok(()) Ok(())
} }
Err(err) => { Err(err) => Err(err),
self.log_and_alert(
LogLevel::Error,
format!("Could not scan current directory: {err}"),
);
Err(err)
}
} }
} }
+1 -1
View File
@@ -80,7 +80,7 @@ impl FileTransferActivity {
self.refresh_remote_status_bar(); self.refresh_remote_status_bar();
// Update components // Update components
self.update_local_filelist(); self.update_local_filelist();
self.update_remote_filelist(); // self.update_remote_filelist();
// Global listener // Global listener
self.mount_global_listener(); self.mount_global_listener();
// Give focus to local explorer // Give focus to local explorer
+15 -23
View File
@@ -56,12 +56,12 @@ static REMOTE_WEBDAV_OPT_REGEX: Lazy<Regex> =
lazy_regex!(r"(?:([^:]+):)(?:(.+[^@])@)(?:([^/]+))(?:(.+))?"); lazy_regex!(r"(?:([^:]+):)(?:(.+[^@])@)(?:([^/]+))(?:(.+))?");
/** /**
* Regex matches: {container}@{pod}/{path} * Regex matches: {namespace}[@{cluster_url}]$/{path}
* - group 1: Container * - group 1: Namespace
* - group 2: Pod * - group 3: Some(cluster_url) | None
* - group 3: Some(path) | None * - group 5: Some(path) | None
*/ */
static REMOTE_KUBE_OPT_REGEX: Lazy<Regex> = lazy_regex!(r"(?:(.+[^@])@)(?:([^/]+))(?:(.+))?"); static REMOTE_KUBE_OPT_REGEX: Lazy<Regex> = lazy_regex!(r"(?:([^@]+))(@(?:([^$]+)))?(\$(?:(.+)))?");
/** /**
* Regex matches: * Regex matches:
@@ -313,23 +313,15 @@ fn parse_s3_remote_opt(s: &str) -> Result<FileTransferParams, String> {
fn parse_kube_remote_opt(s: &str) -> Result<FileTransferParams, String> { fn parse_kube_remote_opt(s: &str) -> Result<FileTransferParams, String> {
match REMOTE_KUBE_OPT_REGEX.captures(s) { match REMOTE_KUBE_OPT_REGEX.captures(s) {
Some(groups) => { Some(groups) => {
let container: String = groups let namespace: Option<String> = groups.get(1).map(|x| x.as_str().to_string());
.get(1) let cluster_url: Option<String> = groups.get(3).map(|x| x.as_str().to_string());
.map(|x| x.as_str().to_string())
.unwrap_or_default();
let pod: String = groups
.get(2)
.map(|x| x.as_str().to_string())
.unwrap_or_default();
let remote_path: Option<PathBuf> = let remote_path: Option<PathBuf> =
groups.get(3).map(|group| PathBuf::from(group.as_str())); groups.get(5).map(|group| PathBuf::from(group.as_str()));
Ok(FileTransferParams::new( Ok(FileTransferParams::new(
FileTransferProtocol::Kube, FileTransferProtocol::Kube,
ProtocolParams::Kube(KubeProtocolParams { ProtocolParams::Kube(KubeProtocolParams {
pod, namespace,
container, cluster_url,
namespace: None,
cluster_url: None,
username: None, username: None,
client_cert: None, client_cert: None,
client_key: None, client_key: None,
@@ -753,13 +745,13 @@ mod tests {
#[test] #[test]
fn should_parse_kube_address() { fn should_parse_kube_address() {
let result = parse_remote_opt("kube://alpine@my-pod/tmp").ok().unwrap(); let result = parse_remote_opt("kube://my-namespace@http://localhost:1234$/tmp")
.ok()
.unwrap();
let params = result.params.kube_params().unwrap(); let params = result.params.kube_params().unwrap();
assert_eq!(params.container.as_str(), "alpine"); assert_eq!(params.namespace, Some("my-namespace".to_string()));
assert_eq!(params.pod.as_str(), "my-pod"); assert_eq!(params.cluster_url.as_deref(), Some("http://localhost:1234"));
assert_eq!(params.namespace, None);
assert_eq!(params.cluster_url, None);
assert_eq!(params.username, None); assert_eq!(params.username, None);
assert_eq!(params.client_cert, None); assert_eq!(params.client_cert, None);
assert_eq!(params.client_key, None); assert_eq!(params.client_key, None);