Skip to content

Commit

Permalink
statfs E2E test
Browse files Browse the repository at this point in the history
  • Loading branch information
t4lz committed Jan 31, 2025
1 parent 16d1dfe commit 31d91dd
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 1 deletion.
3 changes: 3 additions & 0 deletions tests/go-e2e-statfs/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module dir_go

go 1.19
Empty file added tests/go-e2e-statfs/go.sum
Empty file.
44 changes: 44 additions & 0 deletions tests/go-e2e-statfs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"encoding/json"
"fmt"
_ "net" // for dynamic linking
"syscall"
)

func main() {
rootPath := "/"
var statfs syscall.Statfs_t
err := syscall.Statfs(rootPath, &statfs)
if err != nil {
fmt.Println("Error:", err)
return
}

// Convert struct to a JSON-friendly format
data := map[string]interface{}{
"bavail": statfs.Bavail,
"bfree": statfs.Bfree,
"blocks": statfs.Blocks,
"bsize": statfs.Bsize,
"ffree": statfs.Ffree,
"files": statfs.Files,
"flags": statfs.Flags,
"frsize": statfs.Frsize,
"fsid": []int64{int64(statfs.Fsid.X__val[0]), int64(statfs.Fsid.X__val[1])}, // Convert fsid to list
"namelen": statfs.Namelen,
"spare": statfs.Spare,
"type": statfs.Type,
}

// Convert to JSON
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println("JSON Encoding Error:", err)
return
}

// Print JSON
fmt.Println(string(jsonData))
}
96 changes: 95 additions & 1 deletion tests/src/file_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ mod file_ops_tests {

use std::time::Duration;

use k8s_openapi::api::core::v1::Pod;
use kube::{api::LogParams, Api, Client};
use rstest::*;
use serde::Deserialize;

use crate::utils::{run_exec_with_target, service, FileOps, KubeService};
use crate::utils::{
go_statfs_service, kube_client, run_exec_with_target, service, FileOps, KubeService,
};

#[cfg_attr(not(any(feature = "ephemeral", feature = "job")), ignore)]
#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -210,4 +215,93 @@ mod file_ops_tests {
let res = process.wait().await;
assert!(res.success());
}

#[derive(Deserialize, Debug)]
struct Statfs {
bavail: u64,
bfree: u64,
blocks: u64,
bsize: u64,
ffree: u64,
files: u64,
flags: u64,
frsize: u64,
fsid: [u64; 2],
namelen: u64,
spare: [u64; 4],
r#type: u64,
}

impl PartialEq for Statfs {
fn eq(&self, other: &Self) -> bool {
// bavail and bfree changes constantly, so they will usually not be the same in the two
// calls, so we just check they're kind of close.
self.bavail / 1024 == other.bavail / 1024
&& self.bfree / 1024 == other.bfree / 1024
&& self.blocks == other.blocks
&& self.bsize == other.bsize
&& self.ffree == other.ffree
&& self.files == other.files
&& self.flags == other.flags
&& self.frsize == other.frsize
&& self.fsid == other.fsid
&& self.namelen == other.namelen
&& self.spare == other.spare
&& self.r#type == other.r#type
}
}

/// Test that after going through all the conversions between the agent and the user program,
/// the statfs values are correct.
/// This is to prevent a regression to a bug we had where because of `statfs`/`statfs64`
/// struct conversions, we were returning an invalid struct to go when it called SYS_statfs.
#[cfg_attr(not(any(feature = "ephemeral", feature = "job")), ignore)]
#[cfg(target_os = "linux")]
#[rstest]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
#[timeout(Duration::from_secs(240))]
pub async fn go_statfs(
#[future] go_statfs_service: KubeService,
#[future] kube_client: Client,
) {
let app = FileOps::GoStatfs;
let service = go_statfs_service.await;
let client = kube_client.await;
let command = app.command();

let mut args = vec!["--fs-mode", "read"];

if cfg!(feature = "ephemeral") {
args.extend(["-e"].into_iter());
}

let mut process = run_exec_with_target(
command,
&service.pod_container_target(),
Some(&service.namespace),
Some(args),
None,
)
.await;
let res = process.wait().await;
assert!(res.success());
let mirrord_statfs_output = process.get_stdout().await;
let statfs_from_mirrord: Statfs = serde_json::from_str(&mirrord_statfs_output).unwrap();

let pod_api = Api::<Pod>::namespaced(client, &service.namespace);
let statfs_from_pod: Statfs = loop {
let logs = pod_api
.logs(&service.pod_name, &LogParams::default())
.await
.unwrap();
println!("{}", logs);
if let Ok(statfs) = serde_json::from_str(&logs) {
break statfs;
}
// It's possible we didn't get all the logs yet, so the json is not valid.
// Wait a bit and fetch the logs again.
tokio::time::sleep(Duration::from_secs(3)).await;
};
assert_eq!(statfs_from_mirrord, statfs_from_pod);
}
}
16 changes: 16 additions & 0 deletions tests/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub enum FileOps {
GoDir21,
GoDir22,
GoDir23,
GoStatfs,
}

#[derive(Debug)]
Expand Down Expand Up @@ -527,6 +528,7 @@ impl FileOps {
FileOps::GoDir21 => vec!["go-e2e-dir/21.go_test_app"],
FileOps::GoDir22 => vec!["go-e2e-dir/22.go_test_app"],
FileOps::GoDir23 => vec!["go-e2e-dir/23.go_test_app"],
FileOps::GoStatfs => vec!["go-e2e-dir/statfs.go_test_app"],
}
}

Expand Down Expand Up @@ -1625,6 +1627,20 @@ pub async fn random_namespace_self_deleting_service(#[future] kube_client: Clien
.await
}

#[fixture]
pub async fn go_statfs_service(#[future] kube_client: Client) -> KubeService {
service(
"default",
"ClusterIP",
// "ghcr.io/metalbear-co/mirrord-node-udp-logger:latest",
"docker.io/t4lz/go-statfs:latest",
"go-statfs",
true,
kube_client,
)
.await
}

pub fn resolve_node_host() -> String {
if (cfg!(target_os = "linux") && !wsl::is_wsl()) || std::env::var("USE_MINIKUBE").is_ok() {
let output = std::process::Command::new("minikube")
Expand Down

0 comments on commit 31d91dd

Please sign in to comment.