Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi pod #1

Merged
merged 4 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# Changelog

- [Changelog](#changelog)
- [0.3.0](#030)
- [0.2.0](#020)
- [0.1.0](#010)

---

## 0.3.0

Released on 29/09/2024

- Added `KubeMultiPodFs` to operate on multiple pod and containers at the same time. See docs for details.
- **BREAKING ‼️** Renamed `KubeFs` to `KubeContainerFs`.

## 0.2.0

Released on 17/07/2024
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license = "MIT"
name = "remotefs-kube"
readme = "README.md"
repository = "https://github.com/veeso/remotefs-rs-kube"
version = "0.2.0"
version = "0.3.0"

[dependencies]
chrono = "^0.4"
Expand All @@ -28,6 +28,7 @@ tokio-util = "0.7"

[dev-dependencies]
env_logger = "^0.11"
k8s-openapi = { version = "0.22", features = ["v1_30"] }
kube = { version = "0.92", features = ["client", "config", "runtime", "ws"] }
pretty_assertions = "1"
rand = "^0.8.4"
Expand Down
67 changes: 60 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<p align="center">~ Remotefs kube client ~</p>

<p align="center">Developed by <a href="https://veeso.github.io/" target="_blank">@veeso</a></p>
<p align="center">Current version: 0.2.0 (17/07/2024)</p>
<p align="center">Current version: 0.3.0 (29/09/2024)</p>

<p align="center">
<a href="https://opensource.org/licenses/MIT"
Expand Down Expand Up @@ -49,23 +49,76 @@ First of all you need to add **remotefs** and the client to your project depende

```toml
remotefs = "^0.2"
remotefs-kube = "^0.2"
remotefs-kube = "^0.3"
```

these features are supported:

- `find`: enable `find()` method for RemoteFs. (*enabled by default*)
- `no-log`: disable logging. By default, this library will log via the `log` crate.

### Kube client
The library provides two different clients:

Here is a basic usage example, with the `Kube` client, which is very similiar to the `Scp` client.
- **KubeMultiPodFs** client
- **KubeContainerFs** client

```rust,ignore
### Kube multi pod client

The MultiPod client gives access to all the pods with their own containers in a namespace.

This client creates an abstract file system with the following structure

- / (root)
- pod-a
- container-a
- / (container-a root)
- /bin
- /home
- ...
- container-b
- / (container-b root)
- ...
- pod-b
- container-c
- / (container-c root)
- ...

So paths have the following structure: `/pod-name/container-name/path/to/file`.

```rust

// import remotefs trait and client
use remotefs::RemoteFs;
use remotefs_kube::KubeMultiPodFs;
use std::path::Path;

let rt = Arc::new(
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap(),
);
let mut client: KubeMultiPodFs = KubeMultiPodFs::new(&rt);

// connect
assert!(client.connect().is_ok());
// get working directory
println!("Wrkdir: {}", client.pwd().ok().unwrap().display());
// change working directory
assert!(client.change_dir(Path::new("/my-pod/alpine/tmp")).is_ok());
// disconnect
assert!(client.disconnect().is_ok());
```

### Kube container client

Here is a basic usage example, with the `KubeContainerFs` client, which is used to connect and interact with a single container on a certain pod. This client gives the entire access to the container file system.

```rust

// import remotefs trait and client
use remotefs::RemoteFs;
use remotefs_ssh::{SshConfigParseRule, SftpFs, SshOpts};
use remotefs_kube::KubeContainerFs;
use std::path::Path;

let rt = Arc::new(
Expand All @@ -74,7 +127,7 @@ let rt = Arc::new(
.build()
.unwrap(),
);
let mut client: KubeFs = KubeFs::new("my-pod", &rt);
let mut client: KubeContainerFs = KubeContainerFs::new("my-pod", "container-name", &rt);

// connect
assert!(client.connect().is_ok());
Expand Down
75 changes: 30 additions & 45 deletions src/client.rs → src/kube_container_fs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! ## SCP
//! ## Kube Container FS
//!
//! Scp remote fs implementation
//! The `KubeContainerFs` client is a client that allows you to interact with a container in a pod.

use std::ops::Range;
use std::path::{Path, PathBuf};
Expand All @@ -27,17 +27,17 @@ static LS_RE: Lazy<Regex> = lazy_regex!(
r#"^([\-ld])([\-rwxsStT]{9})\s+(\d+)\s+(.+)\s+(.+)\s+(\d+)\s+(\w{3}\s+\d{1,2}\s+(?:\d{1,2}:\d{1,2}|\d{4}))\s+(.+)$"#
);

/// Kube "filesystem" client
pub struct KubeFs {
config: Option<Config>,
container: String,
pod_name: String,
pods: Option<Api<Pod>>,
/// Kube "filesystem" client to interact with a container in a pod
pub struct KubeContainerFs {
pub(crate) config: Option<Config>,
pub(crate) container: String,
pub(crate) pod_name: String,
pub(crate) pods: Option<Api<Pod>>,
runtime: Arc<Runtime>,
wrkdir: PathBuf,
pub(crate) wrkdir: PathBuf,
}

impl KubeFs {
impl KubeContainerFs {
/// Creates a new `KubeFs`
///
/// If `config()` is not called then, it will try to use the configuration from the default kubeconfig file
Expand Down Expand Up @@ -321,7 +321,7 @@ impl KubeFs {
}
}

impl RemoteFs for KubeFs {
impl RemoteFs for KubeContainerFs {
fn connect(&mut self) -> RemoteResult<Welcome> {
debug!("Initializing Kube connection...");
let api = self.runtime.block_on(async {
Expand Down Expand Up @@ -863,7 +863,7 @@ mod test {
.build()
.unwrap(),
);
let mut client = KubeFs::new("test", "test", &rt);
let mut client = KubeContainerFs::new("test", "test", &rt);
assert!(client.config.is_none());
assert_eq!(client.is_connected(), false);
}
Expand All @@ -876,7 +876,7 @@ mod test {
.build()
.unwrap(),
);
let mut client = KubeFs::new("aaaaaa", "test", &rt);
let mut client = KubeContainerFs::new("aaaaaa", "test", &rt);
assert!(client.connect().is_err());
}

Expand Down Expand Up @@ -1490,7 +1490,7 @@ mod test {
.build()
.unwrap(),
);
let client = KubeFs::new("test", "test", &rt);
let client = KubeContainerFs::new("test", "test", &rt);
assert_eq!(
client.get_name_and_link("Cargo.toml"),
(String::from("Cargo.toml"), None)
Expand All @@ -1509,7 +1509,7 @@ mod test {
.build()
.unwrap(),
);
let client = KubeFs::new("test", "test", &rt);
let client = KubeContainerFs::new("test", "test", &rt);
// File
let entry = client
.parse_ls_output(
Expand Down Expand Up @@ -1550,7 +1550,7 @@ mod test {
.build()
.unwrap(),
);
let client = KubeFs::new("test", "test", &rt);
let client = KubeContainerFs::new("test", "test", &rt);
// Directory
let entry = client
.parse_ls_output(
Expand Down Expand Up @@ -1595,7 +1595,7 @@ mod test {
.build()
.unwrap(),
);
let client = KubeFs::new("test", "test", &rt);
let client = KubeContainerFs::new("test", "test", &rt);
// File
let entry = client
.parse_ls_output(
Expand Down Expand Up @@ -1623,7 +1623,7 @@ mod test {
.build()
.unwrap(),
);
let client = KubeFs::new("test", "test", &rt);
let client = KubeContainerFs::new("test", "test", &rt);
assert!(client
.parse_ls_output(
Path::new("/tmp"),
Expand Down Expand Up @@ -1660,7 +1660,7 @@ mod test {
.build()
.unwrap(),
);
let mut client = KubeFs::new("test", "test", &rt);
let mut client = KubeContainerFs::new("test", "test", &rt);
assert!(client.change_dir(Path::new("/tmp")).is_err());
assert!(client
.copy(Path::new("/nowhere"), PathBuf::from("/culonia").as_path())
Expand Down Expand Up @@ -1693,7 +1693,7 @@ mod test {
// -- test utils

#[cfg(feature = "integration-tests")]
fn setup_client() -> (Api<Pod>, KubeFs) {
fn setup_client() -> (Api<Pod>, KubeContainerFs) {
// setup pod with random name

use kube::api::PostParams;
Expand Down Expand Up @@ -1784,7 +1784,7 @@ mod test {
pods
});

let mut client = KubeFs::new(&pod_name, "alpine", &runtime).config(config.clone());
let mut client = KubeContainerFs::new(&pod_name, "alpine", &runtime).config(config.clone());
client.connect().expect("connection failed");
// Create wrkdir
let tempdir = PathBuf::from(generate_tempdir());
Expand All @@ -1799,37 +1799,22 @@ mod test {
}

#[cfg(feature = "integration-tests")]
fn finalize_client(pods: Api<Pod>, mut client: KubeFs) {
// Get working directory

use kube::api::DeleteParams;
use kube::ResourceExt as _;
let wrkdir = client.pwd().ok().unwrap();
// Remove directory
assert!(client.remove_dir_all(wrkdir.as_path()).is_ok());
fn finalize_client(_pods: Api<Pod>, mut client: KubeContainerFs) {
assert!(client.disconnect().is_ok());

// cleanup pods
let pod_name = client.pod_name;
client.runtime.block_on(async {
let dp = DeleteParams::default();
pods.delete(&pod_name, &dp).await.unwrap().map_left(|pdel| {
info!("Deleting {pod_name} pod started: {:?}", pdel);
assert_eq!(pdel.name_any(), pod_name);
});
})
}

#[cfg(feature = "integration-tests")]
fn generate_pod_name() -> String {
use rand::distributions::{Alphanumeric, DistString};
use rand::thread_rng;
let random_string: String = Alphanumeric
.sample_string(&mut thread_rng(), 8)
.chars()
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng as _};

let mut rng = thread_rng();
let random_string: String = std::iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.filter(|c| c.is_alphabetic())
.map(|c| c.to_ascii_lowercase())
.take(8)
.take(12)
.collect();

format!("test-{}", random_string)
Expand Down
Loading
Loading