Skip to content

Commit

Permalink
feat: Added container processes tab
Browse files Browse the repository at this point in the history
  • Loading branch information
ropali committed Nov 25, 2024
1 parent 090796a commit a68b574
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 20 deletions.
19 changes: 18 additions & 1 deletion src-tauri/src/commands/container.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::state::AppState;
use crate::utils::storage::get_user_download_dir;
use crate::utils::terminal::{get_terminal, open_terminal};
use bollard::container::{ListContainersOptions, LogsOptions, RemoveContainerOptions, RenameContainerOptions, StatsOptions};
use bollard::container::{ListContainersOptions, LogsOptions, RemoveContainerOptions, RenameContainerOptions, StatsOptions, TopOptions};
use bollard::models::{ContainerInspectResponse, ContainerSummary};
use futures_util::StreamExt;
use std::collections::HashMap;
Expand Down Expand Up @@ -268,4 +268,21 @@ pub async fn export_container(state: tauri::State<'_, AppState>, name: String) -
}
}
}
}


#[tauri::command]
pub async fn get_container_processes(state: tauri::State<'_, AppState>, container: String) -> Result<Vec<Vec<String>>, String> {
let options = Some(TopOptions {
ps_args: "aux",
});

let result = state.docker.top_processes(&container, options).await;

match result {
Ok(processes) => {
Ok(processes.processes.expect("Failed to get processes from docker container"))
},
Err(e) => return Err(e.to_string()),
}
}
3 changes: 2 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use crate::commands::container::{container_operation, container_stats, delete_container, export_container, fetch_container_info, fetch_containers, get_container, rename_container, stream_docker_logs};
use crate::commands::container::{container_operation, container_stats, delete_container, export_container, fetch_container_info, fetch_containers, get_container, get_container_processes, rename_container, stream_docker_logs};
use crate::commands::extra::{cancel_stream, get_version, ping};
use crate::commands::image::{delete_image, export_image, image_history, image_info, list_images};
use crate::commands::network::{inspect_network, list_networks};
Expand Down Expand Up @@ -51,6 +51,7 @@ fn main() {
rename_container,
export_container,
delete_container,
get_container_processes
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down
25 changes: 7 additions & 18 deletions src/components/Containers/ContainerDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import JSONSyntaxHighlighter from "../JSONSyntaxHighlighter.jsx";
import ContainerNameWidget from "./ContainerNameWidget.jsx";
import Swal from "sweetalert2";
import toast from "../../utils/toast.js";
import ContainerProcesses from "./ContainerProcesses.jsx";


function ContainerDetails() {
Expand Down Expand Up @@ -139,8 +140,6 @@ function ContainerDetails() {
return null;
}

console.log("---VA", result.value)

setLoadingButton("delete")

invoke('delete_container', {
Expand Down Expand Up @@ -199,22 +198,6 @@ function ContainerDetails() {
});
}

const downloadFromContainer = () => {
setLoadingButton("export")
invoke('download_from_container', {
name: selectedContainer.Names[0].replace("/", ""),

}).then((res) => {
if (res) {
toast.success(res);
refreshSelectedContainer()
}
}).catch((e) => {
toast.error(e);
}).finally(() => {
setLoadingButton(null)
});
}

const renderContent = () => {
switch (activeTab) {
Expand All @@ -224,6 +207,9 @@ function ContainerDetails() {
return <JSONSyntaxHighlighter id="json-pretty" json={info}></JSONSyntaxHighlighter>;
case 'STATS':
return <ContainerStats selectedContainer={selectedContainer}/>;

case 'PROCESSES':
return <ContainerProcesses/>
default:
return null;
}
Expand Down Expand Up @@ -326,6 +312,9 @@ function ContainerDetails() {
<button className={`mr-4 pb-2 ${activeTab === 'STATS' ? 'border-b-2 border-base-content' : ''}`}
onClick={() => setActiveTab('STATS')}>STATS
</button>
<button className={`mr-4 pb-2 ${activeTab === 'PROCESSES' ? 'border-b-2 border-base-content' : ''}`}
onClick={() => setActiveTab('PROCESSES')}>PROCESSES
</button>
</div>
<div className="flex-1 overflow-auto text-black p-2 rounded">
{renderContent()}
Expand Down
82 changes: 82 additions & 0 deletions src/components/Containers/ContainerProcesses.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, {useEffect, useState} from 'react';
import {invoke} from "@tauri-apps/api";
import toast from "../../utils/toast.js";
import {useContainers} from "../../state/ContainerContext.jsx";
import LogoScreen from "../LogoScreen.jsx";

const ContainerProcesses = () => {
const {selectedContainer} = useContainers();
const [processes, setProcess] = useState([]);

const isContainerRunning = () => {
return (
selectedContainer != null &&
selectedContainer?.Status.toLowerCase().includes("up")
);
};

const getProcesses = async () => {
try {
const response = await invoke("get_container_processes", {
container: selectedContainer.Names[0].replace("/", ""),
});
setProcess(response);
} catch (error) {
toast.error(error);
}
};

useEffect(() => {
let intervalId;

if (isContainerRunning()) {
getProcesses();
intervalId = setInterval(() => {
getProcesses();
}, 10000); // Calls getProcesses every 10 seconds
}

return () => {
if (intervalId) {
clearInterval(intervalId); // Clears the interval when the component unmounts
}
};
}, [selectedContainer]);

if (!isContainerRunning()) {
return <LogoScreen message={"Container is not running!"}/>;
}

return (
<div className="overflow-x-auto h-full">
<table className="table table-sm w-full bg-base-100 text-base">
<thead>
<tr>
<th>UUID</th>
<th>PID</th>
<th>PPID</th>
<th className="min-w-12">C</th>
<th>TTY</th>
<th>TIME</th>
<th>CMD</th>
</tr>
</thead>
<tbody>
{processes?.map((process, index) => (
<tr key={index} className="text-base-content">
<td>{process[0]}</td>
<td>{process[1]}</td>
<td>{process[2]}</td>
<td>{process[3]}</td>
<td>{process[4]}</td>
<td>{process[5]}</td>
<td>{process[6]}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};

export default ContainerProcesses;

0 comments on commit a68b574

Please sign in to comment.