use std::{fs, fs::File};
use client::{
services::v1::{
container::Runtime, containers_client::ContainersClient, tasks_client::TasksClient,
Container, CreateContainerRequest, CreateTaskRequest, DeleteContainerRequest,
DeleteTaskRequest, StartRequest, WaitRequest,
},
with_namespace,
};
use containerd_client as client;
use prost_types::Any;
use tonic::Request;
const CID: &str = "abc123";
const NAMESPACE: &str = "default";
#[tokio::main(flavor = "current_thread")]
async fn main() {
let channel = client::connect("/run/containerd/containerd.sock")
.await
.expect("Connect Failed");
let mut client = ContainersClient::new(channel.clone());
let rootfs = "/tmp/busybox/bundle/rootfs";
let output = "hello rust client";
let spec = include_str!("container_spec.json");
let spec = spec
.to_string()
.replace("$ROOTFS", rootfs)
.replace("$OUTPUT", output);
let spec = Any {
type_url: "types.containerd.io/opencontainers/runtime-spec/1/Spec".to_string(),
value: spec.into_bytes(),
};
let container = Container {
id: CID.to_string(),
image: "docker.io/library/alpine:latest".to_string(),
runtime: Some(Runtime {
name: "io.containerd.runc.v2".to_string(),
options: None,
}),
spec: Some(spec),
..Default::default()
};
let req = CreateContainerRequest {
container: Some(container),
};
let req = with_namespace!(req, NAMESPACE);
let _resp = client
.create(req)
.await
.expect("Failed to create container");
println!("Container: {:?} created", CID);
let tmp = std::env::temp_dir().join("containerd-client-test");
fs::create_dir_all(&tmp).expect("Failed to create temp directory");
let stdin = tmp.join("stdin");
let stdout = tmp.join("stdout");
let stderr = tmp.join("stderr");
File::create(&stdin).expect("Failed to create stdin");
File::create(&stdout).expect("Failed to create stdout");
File::create(&stderr).expect("Failed to create stderr");
let mut client = TasksClient::new(channel.clone());
let req = CreateTaskRequest {
container_id: CID.to_string(),
stdin: stdin.to_str().unwrap().to_string(),
stdout: stdout.to_str().unwrap().to_string(),
stderr: stderr.to_str().unwrap().to_string(),
..Default::default()
};
let req = with_namespace!(req, NAMESPACE);
let _resp = client.create(req).await.expect("Failed to create task");
println!("Task: {:?} created", CID);
let req = StartRequest {
container_id: CID.to_string(),
..Default::default()
};
let req = with_namespace!(req, NAMESPACE);
let _resp = client.start(req).await.expect("Failed to start task");
println!("Task: {:?} started", CID);
let req = WaitRequest {
container_id: CID.to_string(),
..Default::default()
};
let req = with_namespace!(req, NAMESPACE);
let _resp = client.wait(req).await.expect("Failed to wait task");
println!("Task: {:?} stopped", CID);
let req = DeleteTaskRequest {
container_id: CID.to_string(),
};
let req = with_namespace!(req, NAMESPACE);
let _resp = client.delete(req).await.expect("Failed to delete task");
println!("Task: {:?} deleted", CID);
let mut client = ContainersClient::new(channel);
let req = DeleteContainerRequest {
id: CID.to_string(),
};
let req = with_namespace!(req, NAMESPACE);
let _resp = client
.delete(req)
.await
.expect("Failed to delete container");
println!("Container: {:?} deleted", CID);
let actual_stdout = fs::read_to_string(stdout).expect("read stdout actual");
assert_eq!(actual_stdout.strip_suffix('\n').unwrap(), output);
let _ = fs::remove_dir_all(tmp);
}