mod challenge;
mod gamebox;
use anyhow::Result;
use bollard::{
Docker, body_full,
query_parameters::{BuildImageOptionsBuilder, RemoveContainerOptions, StopContainerOptions},
secret::BuildInfo,
};
#[allow(deprecated)]
use bollard::{network::CreateNetworkOptions, secret::Ipam};
pub use challenge::{ChallengeMeta, DockerMeta, FlagMeta};
use futures_util::StreamExt;
pub use gamebox::{GameBoxConfig, GameBoxMeta};
use std::collections::HashMap;
use std::fs::File;
use std::{io::Read, path::PathBuf};
use tar::Builder;
use tempfile::NamedTempFile;
use tokio_util::bytes::Bytes;
use tracing::{error, info, warn};
pub async fn stop_and_remove(docker: &bollard::Docker, identifier: &str) -> Result<()> {
let stop_options = StopContainerOptions {
t: 0.into(), ..Default::default()
};
let container_name = identifier;
if let Err(e) = docker
.stop_container(container_name, Some(stop_options))
.await
{
warn!(
"⚠️ 停止容器 {} 时提示: {} (可能是已经停止了)",
identifier, e
);
}
let remove_options = RemoveContainerOptions {
v: true, force: true, link: false,
};
match docker
.remove_container(container_name, Some(remove_options))
.await
{
Ok(_) => info!("🗑️ 容器 {} 已彻底清理", identifier),
Err(e) => {
error!("❌ 删除容器 {} 时出错: {}", identifier, e);
}
}
Ok(())
}
pub async fn remove_and_create_bridge_net(
docker: &Docker,
bridge_name: String,
cidr: String,
) -> anyhow::Result<()> {
let _ = docker.remove_network(&bridge_name).await;
let mut network_options = HashMap::new();
network_options.insert(
"com.docker.network.bridge.name".to_string(),
bridge_name.clone(),
);
#[allow(deprecated)] let conf = CreateNetworkOptions {
name: bridge_name,
driver: "bridge".to_string(),
internal: true,
check_duplicate: true,
ipam: Ipam {
config: Some(vec![bollard::secret::IpamConfig {
subnet: cidr.into(),
..Default::default()
}]),
..Default::default()
},
options: network_options,
..Default::default()
};
docker.create_network(conf).await?;
Ok(())
}
pub async fn build_image(
docker: &Docker,
image_tag: &str,
context_path: &PathBuf,
) -> anyhow::Result<()> {
let options = BuildImageOptionsBuilder::default().t(image_tag).build();
let tmp = NamedTempFile::new()?;
{
let file = File::create(tmp.path())?;
let mut tar_builder = Builder::new(file);
tar_builder.append_dir_all(".", context_path)?; tar_builder.finish()?;
}
let mut buf = Vec::new();
File::open(tmp.path())?.read_to_end(&mut buf)?;
let body = body_full(Bytes::from(buf));
let mut build_stream = docker.build_image(options, None, Some(body));
let mut infos = Vec::new();
while let Some(update) = build_stream.next().await {
let info: BuildInfo = update?;
if let Some(ref stream_msg) = info.stream {
infos.push(stream_msg.trim().to_owned());
}
if let Some(ref err) = info.error {
error!("ERROR: {}", err);
}
}
for info in infos {
println!("{}", info);
}
Ok(())
}