feat: kube protocol support (#267)

This commit is contained in:
Christian Visintin
2024-07-17 11:59:30 +02:00
committed by GitHub
parent cf529c1678
commit f757336d75
50 changed files with 1863 additions and 418 deletions

View File

@@ -5,7 +5,8 @@
// Locals
use super::{AuthActivity, FileTransferParams};
use crate::filetransfer::params::{
AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams, WebDAVProtocolParams,
AwsS3Params, GenericProtocolParams, KubeProtocolParams, ProtocolParams, SmbParams,
WebDAVProtocolParams,
};
impl AuthActivity {
@@ -164,6 +165,8 @@ impl AuthActivity {
);
match bookmark.params {
ProtocolParams::AwsS3(params) => self.load_bookmark_s3_into_gui(params),
ProtocolParams::Kube(params) => self.load_bookmark_kube_into_gui(params),
ProtocolParams::Generic(params) => self.load_bookmark_generic_into_gui(params),
ProtocolParams::Smb(params) => self.load_bookmark_smb_into_gui(params),
ProtocolParams::WebDAV(params) => self.load_bookmark_webdav_into_gui(params),
@@ -189,6 +192,16 @@ impl AuthActivity {
self.mount_s3_new_path_style(params.new_path_style);
}
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(""));
self.mount_kube_client_key(params.client_key.as_deref().unwrap_or(""));
self.mount_kube_username(params.username.as_deref().unwrap_or(""));
}
fn load_bookmark_smb_into_gui(&mut self, params: SmbParams) {
self.mount_address(params.address.as_str());
#[cfg(unix)]

View File

@@ -10,8 +10,8 @@ use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
use super::{FileTransferProtocol, FormMsg, Msg, UiMsg};
use crate::ui::activities::auth::{
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_S3, RADIO_PROTOCOL_SCP,
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_KUBE, RADIO_PROTOCOL_S3,
RADIO_PROTOCOL_SCP, RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
};
// -- protocol
@@ -31,9 +31,9 @@ impl ProtocolRadio {
.modifiers(BorderType::Rounded),
)
.choices(if cfg!(smb) {
&["SFTP", "SCP", "FTP", "FTPS", "S3", "WebDAV", "SMB"]
&["SFTP", "SCP", "FTP", "FTPS", "S3", "Kube", "WebDAV", "SMB"]
} else {
&["SFTP", "SCP", "FTP", "FTPS", "S3", "WebDAV"]
&["SFTP", "SCP", "FTP", "FTPS", "S3", "Kube", "WebDAV"]
})
.foreground(color)
.rewind(true)
@@ -50,6 +50,7 @@ impl ProtocolRadio {
RADIO_PROTOCOL_FTPS => FileTransferProtocol::Ftp(true),
RADIO_PROTOCOL_S3 => FileTransferProtocol::AwsS3,
RADIO_PROTOCOL_SMB => FileTransferProtocol::Smb,
RADIO_PROTOCOL_KUBE => FileTransferProtocol::Kube,
RADIO_PROTOCOL_WEBDAV => FileTransferProtocol::WebDAV,
_ => FileTransferProtocol::Sftp,
}
@@ -63,6 +64,7 @@ impl ProtocolRadio {
FileTransferProtocol::Ftp(false) => RADIO_PROTOCOL_FTP,
FileTransferProtocol::Ftp(true) => RADIO_PROTOCOL_FTPS,
FileTransferProtocol::AwsS3 => RADIO_PROTOCOL_S3,
FileTransferProtocol::Kube => RADIO_PROTOCOL_KUBE,
FileTransferProtocol::Smb => RADIO_PROTOCOL_SMB,
FileTransferProtocol::WebDAV => RADIO_PROTOCOL_WEBDAV,
}
@@ -827,3 +829,252 @@ 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,
}
impl InputKubeNamespace {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder("namespace", Style::default().fg(Color::Rgb(128, 128, 128)))
.title("Pod namespace (optional)", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubeNamespace {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubeNamespaceBlurDown),
Msg::Ui(UiMsg::KubeNamespaceBlurUp),
)
}
}
#[derive(MockComponent)]
pub struct InputKubeClusterUrl {
component: Input,
}
impl InputKubeClusterUrl {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder(
"cluster url",
Style::default().fg(Color::Rgb(128, 128, 128)),
)
.title("Kube cluster url (optional)", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubeClusterUrl {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubeClusterUrlBlurDown),
Msg::Ui(UiMsg::KubeClusterUrlBlurUp),
)
}
}
#[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,
}
impl InputKubeUsername {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder("username", Style::default().fg(Color::Rgb(128, 128, 128)))
.title("Kube username (optional)", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubeUsername {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubeUsernameBlurDown),
Msg::Ui(UiMsg::KubeUsernameBlurUp),
)
}
}
#[derive(MockComponent)]
pub struct InputKubeClientCert {
component: Input,
}
impl InputKubeClientCert {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder(
"/home/user/.kube/client.crt",
Style::default().fg(Color::Rgb(128, 128, 128)),
)
.title("Kube client cert path (optional)", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubeClientCert {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubeClientCertBlurDown),
Msg::Ui(UiMsg::KubeClientCertBlurUp),
)
}
}
#[derive(MockComponent)]
pub struct InputKubeClientKey {
component: Input,
}
impl InputKubeClientKey {
pub fn new(bucket: &str, color: Color) -> Self {
Self {
component: Input::default()
.borders(
Borders::default()
.color(color)
.modifiers(BorderType::Rounded),
)
.foreground(color)
.placeholder(
"/home/user/.kube/client.key",
Style::default().fg(Color::Rgb(128, 128, 128)),
)
.title("Kube client key path (optional)", Alignment::Left)
.input_type(InputType::Text)
.value(bucket),
}
}
}
impl Component<Msg, NoUserEvent> for InputKubeClientKey {
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
handle_input_ev(
self,
ev,
Msg::Ui(UiMsg::KubeClientKeyBlurDown),
Msg::Ui(UiMsg::KubeClientKeyBlurUp),
)
}
}

View File

@@ -16,10 +16,12 @@ pub use bookmarks::{
#[cfg(unix)]
pub use form::InputSmbWorkgroup;
pub use form::{
InputAddress, InputLocalDirectory, InputPassword, InputPort, InputRemoteDirectory,
InputS3AccessKey, InputS3Bucket, InputS3Endpoint, InputS3Profile, InputS3Region,
InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken, InputSmbShare,
InputUsername, InputWebDAVUri, ProtocolRadio, RadioS3NewPathStyle,
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,
};
pub use popup::{
ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup,

View File

@@ -14,6 +14,7 @@ impl AuthActivity {
FileTransferProtocol::Sftp | FileTransferProtocol::Scp => 22,
FileTransferProtocol::Ftp(_) => 21,
FileTransferProtocol::AwsS3 => 22, // Doesn't matter, since not used
FileTransferProtocol::Kube => 22, // Doesn't matter, since not used
FileTransferProtocol::Smb => 445,
FileTransferProtocol::WebDAV => 80, // Doesn't matter, since not used
}
@@ -38,6 +39,7 @@ impl AuthActivity {
pub(super) fn collect_host_params(&self) -> Result<FileTransferParams, &'static str> {
match self.protocol {
FileTransferProtocol::AwsS3 => self.collect_s3_host_params(),
FileTransferProtocol::Kube => self.collect_kube_host_params(),
FileTransferProtocol::Smb => self.collect_smb_host_params(),
FileTransferProtocol::Ftp(_)
| FileTransferProtocol::Scp
@@ -80,6 +82,20 @@ 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),
local_path: self.get_input_local_directory(),
remote_path: self.get_input_remote_directory(),
})
}
pub(super) fn collect_smb_host_params(&self) -> Result<FileTransferParams, &'static str> {
let params = self.get_smb_params_input();
if params.address.is_empty() {

View File

@@ -29,8 +29,9 @@ const RADIO_PROTOCOL_SCP: usize = 1;
const RADIO_PROTOCOL_FTP: usize = 2;
const RADIO_PROTOCOL_FTPS: usize = 3;
const RADIO_PROTOCOL_S3: usize = 4;
const RADIO_PROTOCOL_WEBDAV: usize = 5;
const RADIO_PROTOCOL_SMB: usize = 6;
const RADIO_PROTOCOL_KUBE: usize = 5;
const RADIO_PROTOCOL_WEBDAV: usize = 6;
const RADIO_PROTOCOL_SMB: usize = 7;
// -- components
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
@@ -47,6 +48,13 @@ pub enum Id {
InfoPopup,
InstallUpdatePopup,
Keybindings,
KubePodName,
KubeContainer,
KubeNamespace,
KubeClusterUrl,
KubeUsername,
KubeClientCert,
KubeClientKey,
LocalDirectory,
NewVersionChangelog,
NewVersionDisclaimer,
@@ -111,6 +119,20 @@ pub enum UiMsg {
CloseKeybindingsPopup,
CloseQuitPopup,
CloseSaveBookmark,
KubePodNameBlurDown,
KubePodNameBlurUp,
KubeContainerBlurDown,
KubeContainerBlurUp,
KubeNamespaceBlurDown,
KubeNamespaceBlurUp,
KubeClusterUrlBlurDown,
KubeClusterUrlBlurUp,
KubeUsernameBlurDown,
KubeUsernameBlurUp,
KubeClientCertBlurDown,
KubeClientCertBlurUp,
KubeClientKeyBlurDown,
KubeClientKeyBlurUp,
LocalDirectoryBlurDown,
LocalDirectoryBlurUp,
ParamsFormBlur,
@@ -167,6 +189,7 @@ pub enum UiMsg {
enum InputMask {
Generic,
AwsS3,
Kube,
Smb,
WebDAV,
}
@@ -244,6 +267,7 @@ impl AuthActivity {
FileTransferProtocol::Ftp(_)
| FileTransferProtocol::Scp
| FileTransferProtocol::Sftp => InputMask::Generic,
FileTransferProtocol::Kube => InputMask::Kube,
FileTransferProtocol::Smb => InputMask::Smb,
FileTransferProtocol::WebDAV => InputMask::WebDAV,
}

View File

@@ -70,6 +70,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Password,
InputMask::Smb => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName,
InputMask::WebDAV => &Id::Password,
})
.is_ok());
@@ -83,6 +84,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Password,
InputMask::Smb => &Id::Password,
InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName,
InputMask::WebDAV => &Id::Password,
})
.is_ok());
@@ -179,6 +181,7 @@ impl AuthActivity {
#[cfg(windows)]
InputMask::Smb => &Id::RemoteDirectory,
InputMask::AwsS3 => panic!("this shouldn't happen (password on s3)"),
InputMask::Kube => panic!("this shouldn't happen (password on kube)"),
InputMask::WebDAV => &Id::RemoteDirectory,
})
.is_ok());
@@ -192,8 +195,8 @@ impl AuthActivity {
.active(match self.input_mask() {
InputMask::Generic => &Id::Username,
InputMask::Smb => &Id::SmbShare,
InputMask::AwsS3 | InputMask::WebDAV =>
panic!("this shouldn't happen (port on s3)"),
InputMask::AwsS3 | InputMask::Kube | InputMask::WebDAV =>
panic!("this shouldn't happen (port on s3/kube/webdav)"),
})
.is_ok());
}
@@ -207,6 +210,7 @@ impl AuthActivity {
InputMask::Generic => &Id::Address,
InputMask::Smb => &Id::Address,
InputMask::AwsS3 => &Id::S3Bucket,
InputMask::Kube => &Id::KubePodName,
InputMask::WebDAV => &Id::WebDAVUri,
})
.is_ok());
@@ -229,6 +233,7 @@ impl AuthActivity {
InputMask::Smb => &Id::SmbWorkgroup,
#[cfg(windows)]
InputMask::Smb => &Id::Password,
InputMask::Kube => &Id::KubeClientKey,
InputMask::AwsS3 => &Id::S3NewPathStyle,
InputMask::WebDAV => &Id::Password,
})
@@ -288,6 +293,48 @@ impl AuthActivity {
UiMsg::S3NewPathStyleBlurUp => {
assert!(self.app.active(&Id::S3SessionToken).is_ok());
}
UiMsg::KubeClientCertBlurDown => {
assert!(self.app.active(&Id::KubeClientKey).is_ok());
}
UiMsg::KubeClientCertBlurUp => {
assert!(self.app.active(&Id::KubeUsername).is_ok());
}
UiMsg::KubeClientKeyBlurDown => {
assert!(self.app.active(&Id::RemoteDirectory).is_ok());
}
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());
}
UiMsg::KubeClusterUrlBlurDown => {
assert!(self.app.active(&Id::KubeUsername).is_ok());
}
UiMsg::KubeClusterUrlBlurUp => {
assert!(self.app.active(&Id::KubeNamespace).is_ok());
}
UiMsg::KubeUsernameBlurDown => {
assert!(self.app.active(&Id::KubeClientCert).is_ok());
}
UiMsg::KubeUsernameBlurUp => {
assert!(self.app.active(&Id::KubeClusterUrl).is_ok());
}
UiMsg::SmbShareBlurDown => {
assert!(self.app.active(&Id::Username).is_ok());
}
@@ -337,6 +384,7 @@ impl AuthActivity {
.active(match self.input_mask() {
InputMask::Generic => &Id::Port,
InputMask::Smb => &Id::SmbShare,
InputMask::Kube => panic!("this shouldn't happen (username on kube)"),
InputMask::AwsS3 => panic!("this shouldn't happen (username on s3)"),
InputMask::WebDAV => &Id::WebDAVUri,
})

View File

@@ -13,7 +13,8 @@ use tuirealm::{State, StateValue, Sub, SubClause, SubEventClause};
use super::{components, AuthActivity, Context, FileTransferProtocol, Id, InputMask};
use crate::filetransfer::params::{
AwsS3Params, GenericProtocolParams, ProtocolParams, SmbParams, WebDAVProtocolParams,
AwsS3Params, GenericProtocolParams, KubeProtocolParams, ProtocolParams, SmbParams,
WebDAVProtocolParams,
};
use crate::filetransfer::FileTransferParams;
use crate::utils::ui::{Popup, Size};
@@ -60,6 +61,13 @@ impl AuthActivity {
self.mount_s3_security_token("");
self.mount_s3_session_token("");
self.mount_s3_new_path_style(false);
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)]
self.mount_smb_workgroup("");
@@ -155,6 +163,16 @@ impl AuthActivity {
)
.direction(Direction::Vertical)
.split(auth_chunks[4]),
InputMask::Kube => Layout::default()
.constraints([
Constraint::Length(3), // ...
Constraint::Length(3), // ...
Constraint::Length(3), // ...
Constraint::Length(3), // ...
Constraint::Length(3), // remote directory
])
.direction(Direction::Vertical)
.split(auth_chunks[4]),
InputMask::Generic => Layout::default()
.constraints(
[
@@ -238,6 +256,13 @@ impl AuthActivity {
self.app.view(&view_ids[2], f, input_mask[2]);
self.app.view(&view_ids[3], f, input_mask[3]);
}
InputMask::Kube => {
let view_ids = self.get_kube_view();
self.app.view(&view_ids[0], f, input_mask[0]);
self.app.view(&view_ids[1], f, input_mask[1]);
self.app.view(&view_ids[2], f, input_mask[2]);
self.app.view(&view_ids[3], f, input_mask[3]);
}
InputMask::Smb => {
let view_ids = self.get_smb_view();
self.app.view(&view_ids[0], f, input_mask[0]);
@@ -791,6 +816,90 @@ 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
.app
.remount(
Id::KubeNamespace,
Box::new(components::InputKubeNamespace::new(value, color)),
vec![]
)
.is_ok());
}
pub(super) fn mount_kube_cluster_url(&mut self, value: &str) {
let color = self.theme().auth_username;
assert!(self
.app
.remount(
Id::KubeClusterUrl,
Box::new(components::InputKubeClusterUrl::new(value, color)),
vec![]
)
.is_ok());
}
pub(super) fn mount_kube_username(&mut self, value: &str) {
let color = self.theme().auth_password;
assert!(self
.app
.remount(
Id::KubeUsername,
Box::new(components::InputKubeUsername::new(value, color)),
vec![]
)
.is_ok());
}
pub(super) fn mount_kube_client_cert(&mut self, value: &str) {
let color = self.theme().auth_address;
assert!(self
.app
.remount(
Id::KubeClientCert,
Box::new(components::InputKubeClientCert::new(value, color)),
vec![]
)
.is_ok());
}
pub(super) fn mount_kube_client_key(&mut self, value: &str) {
let color = self.theme().auth_port;
assert!(self
.app
.remount(
Id::KubeClientKey,
Box::new(components::InputKubeClientKey::new(value, color)),
vec![]
)
.is_ok());
}
pub(crate) fn mount_smb_share(&mut self, share: &str) {
let color = self.theme().auth_password;
assert!(self
@@ -863,6 +972,26 @@ impl AuthActivity {
.new_path_style(new_path_style)
}
/// 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,
client_cert,
client_key,
}
}
/// Collect s3 input values from view
#[cfg(unix)]
pub(super) fn get_smb_params_input(&self) -> SmbParams {
@@ -1025,6 +1154,55 @@ 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),
_ => None,
}
}
pub(super) fn get_input_kube_cluster_url(&self) -> Option<String> {
match self.app.state(&Id::KubeClusterUrl) {
Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x),
_ => None,
}
}
pub(super) fn get_input_kube_username(&self) -> Option<String> {
match self.app.state(&Id::KubeUsername) {
Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x),
_ => None,
}
}
pub(super) fn get_input_kube_client_cert(&self) -> Option<String> {
match self.app.state(&Id::KubeClientCert) {
Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x),
_ => None,
}
}
pub(super) fn get_input_kube_client_key(&self) -> Option<String> {
match self.app.state(&Id::KubeClientKey) {
Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x),
_ => None,
}
}
pub(super) fn get_input_smb_share(&self) -> String {
match self.app.state(&Id::SmbShare) {
Ok(State::One(StateValue::String(x))) => x,
@@ -1063,6 +1241,7 @@ impl AuthActivity {
match self.input_mask() {
InputMask::AwsS3 => 12,
InputMask::Generic => 12,
InputMask::Kube => 12,
InputMask::Smb => 12,
InputMask::WebDAV => 12,
}
@@ -1104,6 +1283,24 @@ impl AuthActivity {
protocol, username, params.address, params.port
)
}
ProtocolParams::Kube(params) => {
format!(
"{}://{}@{}{}{}",
protocol,
params.container,
params.pod,
params
.namespace
.as_deref()
.map(|x| format!("/{x}"))
.unwrap_or_default(),
params
.cluster_url
.as_deref()
.map(|x| format!("@{x}"))
.unwrap_or_default()
)
}
#[cfg(unix)]
ProtocolParams::Smb(params) => {
let username: String = match params.username {
@@ -1189,6 +1386,54 @@ 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,
Id::KubeUsername,
Id::KubeClientCert,
],
Some(&Id::KubeClientKey) => [
Id::KubeClusterUrl,
Id::KubeUsername,
Id::KubeClientCert,
Id::KubeClientKey,
],
Some(&Id::RemoteDirectory) => [
Id::KubeUsername,
Id::KubeClientCert,
Id::KubeClientKey,
Id::RemoteDirectory,
],
Some(&Id::LocalDirectory) => [
Id::KubeClientCert,
Id::KubeClientKey,
Id::RemoteDirectory,
Id::LocalDirectory,
],
_ => [
Id::KubePodName,
Id::KubeContainer,
Id::KubeNamespace,
Id::KubeClusterUrl,
],
}
}
#[cfg(unix)]
fn get_smb_view(&self) -> [Id; 4] {
match self.app.focus() {

View File

@@ -111,6 +111,7 @@ 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::Smb(params) => params.address.clone(),
ProtocolParams::WebDAV(params) => params.uri.clone(),
}
@@ -135,6 +136,13 @@ 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)
}
ProtocolParams::Smb(params) => {
info!(
"Client is not connected to remote; connecting to {}:{}",

View File

@@ -12,8 +12,8 @@ use super::{ConfigMsg, Msg};
use crate::explorer::GroupDirs as GroupDirsEnum;
use crate::filetransfer::FileTransferProtocol;
use crate::ui::activities::setup::{
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_S3, RADIO_PROTOCOL_SCP,
RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
RADIO_PROTOCOL_FTP, RADIO_PROTOCOL_FTPS, RADIO_PROTOCOL_KUBE, RADIO_PROTOCOL_S3,
RADIO_PROTOCOL_SCP, RADIO_PROTOCOL_SFTP, RADIO_PROTOCOL_SMB, RADIO_PROTOCOL_WEBDAV,
};
use crate::utils::parser::parse_bytesize;
@@ -67,7 +67,7 @@ impl DefaultProtocol {
.color(Color::Cyan)
.modifiers(BorderType::Rounded),
)
.choices(&["SFTP", "SCP", "FTP", "FTPS", "S3", "SMB", "WebDAV"])
.choices(&["SFTP", "SCP", "FTP", "FTPS", "Kube", "S3", "SMB", "WebDAV"])
.foreground(Color::Cyan)
.rewind(true)
.title("Default protocol", Alignment::Left)
@@ -76,6 +76,7 @@ impl DefaultProtocol {
FileTransferProtocol::Scp => RADIO_PROTOCOL_SCP,
FileTransferProtocol::Ftp(false) => RADIO_PROTOCOL_FTP,
FileTransferProtocol::Ftp(true) => RADIO_PROTOCOL_FTPS,
FileTransferProtocol::Kube => RADIO_PROTOCOL_KUBE,
FileTransferProtocol::AwsS3 => RADIO_PROTOCOL_S3,
FileTransferProtocol::Smb => RADIO_PROTOCOL_SMB,
FileTransferProtocol::WebDAV => RADIO_PROTOCOL_WEBDAV,

View File

@@ -29,9 +29,10 @@ const RADIO_PROTOCOL_SFTP: usize = 0;
const RADIO_PROTOCOL_SCP: usize = 1;
const RADIO_PROTOCOL_FTP: usize = 2;
const RADIO_PROTOCOL_FTPS: usize = 3;
const RADIO_PROTOCOL_S3: usize = 4;
const RADIO_PROTOCOL_SMB: usize = 5;
const RADIO_PROTOCOL_WEBDAV: usize = 6;
const RADIO_PROTOCOL_KUBE: usize = 4;
const RADIO_PROTOCOL_S3: usize = 5;
const RADIO_PROTOCOL_SMB: usize = 6;
const RADIO_PROTOCOL_WEBDAV: usize = 7;
// -- components
#[derive(Debug, Eq, PartialEq, Clone, Hash)]

View File

@@ -11,7 +11,8 @@ use tuirealm::tui::layout::{Constraint, Direction, Layout};
use tuirealm::{State, StateValue};
use super::{
components, Context, Id, IdCommon, IdConfig, SetupActivity, ViewLayout, RADIO_PROTOCOL_WEBDAV,
components, Context, Id, IdCommon, IdConfig, SetupActivity, ViewLayout, RADIO_PROTOCOL_KUBE,
RADIO_PROTOCOL_WEBDAV,
};
use crate::explorer::GroupDirs;
use crate::filetransfer::FileTransferProtocol;
@@ -277,6 +278,7 @@ impl SetupActivity {
RADIO_PROTOCOL_SCP => FileTransferProtocol::Scp,
RADIO_PROTOCOL_FTP => FileTransferProtocol::Ftp(false),
RADIO_PROTOCOL_FTPS => FileTransferProtocol::Ftp(true),
RADIO_PROTOCOL_KUBE => FileTransferProtocol::Kube,
RADIO_PROTOCOL_S3 => FileTransferProtocol::AwsS3,
RADIO_PROTOCOL_SMB => FileTransferProtocol::Smb,
RADIO_PROTOCOL_WEBDAV => FileTransferProtocol::WebDAV,