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

@@ -41,6 +41,10 @@
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
Released on 17/07/2024

83
Cargo.lock generated
View File

@@ -568,6 +568,19 @@ dependencies = [
"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]]
name = "debug-helper"
version = "0.3.13"
@@ -1450,6 +1463,12 @@ name = "keyring"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fa83d1ca02db069b5fbe94b23b584d588e989218310c9c15015bb5571ef1a94"
dependencies = [
"byteorder",
"dbus-secret-service",
"security-framework",
"windows-sys 0.59.0",
]
[[package]]
name = "kqueue"
@@ -1809,12 +1828,76 @@ dependencies = [
"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]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "num-traits"
version = "0.2.19"

View File

@@ -46,7 +46,7 @@ dirs = "^5.0"
edit = "^0.1"
filetime = "^0.2"
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_static = "^1"
log = "^0.4"

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
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### SMB Adressargument

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
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### Argumento de dirección de WebDAV

View File

@@ -108,7 +108,7 @@ s3://buckethead@eu-central-1:default:/assets
Si vous souhaitez vous connecter à Kube, utilisez la syntaxe suivante
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### Argument d'adresse WebDAV

View File

@@ -106,7 +106,7 @@ s3://buckethead@eu-central-1:default:/assets
Nel caso tu voglia connetterti a Kube usa la seguente sintassi
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### Argomento indirizzo per WebDAV

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
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### WebDAV address argument

View File

@@ -111,7 +111,7 @@ s3://buckethead@eu-central-1:default:/assets
Caso queira se conectar ao Kube, use a seguinte sintaxe
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### Argumento de Endereço do WebDAV

View File

@@ -108,7 +108,7 @@ s3://buckethead@eu-central-1:default:/assets
如果您想连接到 Kube请使用以下语法
```txt
kube://<container>@<pod></path>
kube://[namespace][@<cluster_url>][$</path>]
```
#### WebDAV 地址参数

View File

@@ -351,8 +351,6 @@ mod tests {
#[test]
fn bookmark_from_kube_ftparams() {
let params = ProtocolParams::Kube(KubeProtocolParams {
pod: "pod".to_string(),
container: "container".to_string(),
namespace: Some("default".to_string()),
username: Some("root".to_string()),
cluster_url: Some("https://localhost:6443".to_string()),
@@ -368,8 +366,6 @@ mod tests {
assert!(bookmark.username.is_none());
assert!(bookmark.password.is_none());
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.cluster_url.as_deref().unwrap(),
@@ -494,8 +490,6 @@ mod tests {
remote_path: Some(PathBuf::from("/tmp")),
local_path: Some(PathBuf::from("/usr")),
kube: Some(KubeParams {
pod_name: String::from("pod"),
container: String::from("container"),
namespace: Some(String::from("default")),
cluster_url: Some(String::from("https://localhost:6443")),
username: Some(String::from("root")),
@@ -516,7 +510,6 @@ mod tests {
std::path::Path::new("/usr")
);
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.cluster_url.as_deref().unwrap(),

View File

@@ -5,8 +5,6 @@ use crate::filetransfer::params::KubeProtocolParams;
/// Extra Connection parameters for Kube protocol
#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Default)]
pub struct KubeParams {
pub pod_name: String,
pub container: String,
pub namespace: Option<String>,
pub cluster_url: Option<String>,
pub username: Option<String>,
@@ -17,8 +15,6 @@ pub struct KubeParams {
impl From<KubeParams> for KubeProtocolParams {
fn from(value: KubeParams) -> Self {
Self {
pod: value.pod_name,
container: value.container,
namespace: value.namespace,
cluster_url: value.cluster_url,
username: value.username,
@@ -31,8 +27,6 @@ impl From<KubeParams> for KubeProtocolParams {
impl From<KubeProtocolParams> for KubeParams {
fn from(value: KubeProtocolParams) -> Self {
Self {
pod_name: value.pod,
container: value.container,
namespace: value.namespace,
cluster_url: value.cluster_url,
username: value.username,

View File

@@ -412,8 +412,6 @@ mod tests {
assert_eq!(host.password, None);
assert_eq!(host.protocol, FileTransferProtocol::Kube);
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.cluster_url.as_deref().unwrap(), "https://my-cluster");
assert_eq!(kube.username.as_deref().unwrap(), "my-username");
@@ -515,8 +513,6 @@ mod tests {
s3: None,
smb: None,
kube: Some(KubeParams {
pod_name: "my-pod".to_string(),
container: "my-container".to_string(),
namespace: Some("my-namespace".to_string()),
cluster_url: Some("https://my-cluster".to_string()),
username: Some("my-username".to_string()),
@@ -593,6 +589,22 @@ mod tests {
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 {
// Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
@@ -617,8 +629,6 @@ mod tests {
[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"
@@ -644,6 +654,29 @@ mod tests {
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 {
// Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();

View File

@@ -8,7 +8,7 @@ use std::sync::Arc;
use remotefs::RemoteFs;
use remotefs_aws_s3::AwsS3Fs;
use remotefs_ftp::FtpFs;
use remotefs_kube::KubeContainerFs as KubeFs;
use remotefs_kube::KubeMultiPodFs as KubeFs;
#[cfg(smb_unix)]
use remotefs_smb::SmbOptions;
#[cfg(smb)]
@@ -119,7 +119,7 @@ impl Builder {
.build()
.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() {
kube_fs.config(config)
} else {
@@ -281,8 +281,6 @@ mod test {
#[test]
fn test_should_build_kube_fs() {
let params = ProtocolParams::Kube(KubeProtocolParams {
pod: "pod".to_string(),
container: "container".to_string(),
namespace: Some("namespace".to_string()),
cluster_url: Some("cluster_url".to_string()),
username: Some("username".to_string()),

View File

@@ -3,8 +3,6 @@ use remotefs_kube::Config;
/// Protocol params used by WebDAV
#[derive(Debug, Clone)]
pub struct KubeProtocolParams {
pub pod: String,
pub container: String,
pub namespace: Option<String>,
pub cluster_url: Option<String>,
pub username: Option<String>,

View File

@@ -76,16 +76,12 @@ impl KeyStorage for KeyringStorage {
#[cfg(test)]
mod tests {
#[cfg(linux)]
use pretty_assertions::assert_eq;
#[cfg(linux)]
use whoami::username;
#[cfg(linux)]
use super::*;
#[test]
#[cfg(linux)]
fn test_system_keys_keyringstorage() {
let username: String = username();
let storage: KeyringStorage = KeyringStorage::new(username.as_str());

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

View File

@@ -56,12 +56,12 @@ static REMOTE_WEBDAV_OPT_REGEX: Lazy<Regex> =
lazy_regex!(r"(?:([^:]+):)(?:(.+[^@])@)(?:([^/]+))(?:(.+))?");
/**
* Regex matches: {container}@{pod}/{path}
* - group 1: Container
* - group 2: Pod
* - group 3: Some(path) | None
* Regex matches: {namespace}[@{cluster_url}]$/{path}
* - group 1: Namespace
* - group 3: Some(cluster_url) | 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:
@@ -313,23 +313,15 @@ fn parse_s3_remote_opt(s: &str) -> Result<FileTransferParams, String> {
fn parse_kube_remote_opt(s: &str) -> Result<FileTransferParams, String> {
match REMOTE_KUBE_OPT_REGEX.captures(s) {
Some(groups) => {
let container: String = groups
.get(1)
.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 namespace: Option<String> = groups.get(1).map(|x| x.as_str().to_string());
let cluster_url: Option<String> = groups.get(3).map(|x| x.as_str().to_string());
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(
FileTransferProtocol::Kube,
ProtocolParams::Kube(KubeProtocolParams {
pod,
container,
namespace: None,
cluster_url: None,
namespace,
cluster_url,
username: None,
client_cert: None,
client_key: None,
@@ -753,13 +745,13 @@ mod tests {
#[test]
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();
assert_eq!(params.container.as_str(), "alpine");
assert_eq!(params.pod.as_str(), "my-pod");
assert_eq!(params.namespace, None);
assert_eq!(params.cluster_url, None);
assert_eq!(params.namespace, Some("my-namespace".to_string()));
assert_eq!(params.cluster_url.as_deref(), Some("http://localhost:1234"));
assert_eq!(params.username, None);
assert_eq!(params.client_cert, None);
assert_eq!(params.client_key, None);