feat(example): added an example of packaging a rust app from github #124
48
Cargo.lock
generated
48
Cargo.lock
generated
@ -1685,6 +1685,16 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "example-try-rust-webapp"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"harmony",
|
||||||
|
"harmony_cli",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "example-tui"
|
name = "example-tui"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1700,6 +1710,15 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "example_validate_ceph_cluster_health"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"harmony",
|
||||||
|
"harmony_cli",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eyre"
|
name = "eyre"
|
||||||
version = "0.6.12"
|
version = "0.6.12"
|
||||||
@ -2097,6 +2116,7 @@ dependencies = [
|
|||||||
"tokio-util",
|
"tokio-util",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4671,6 +4691,15 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.27"
|
version = "0.1.27"
|
||||||
@ -5961,6 +5990,16 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -6115,6 +6154,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -68,6 +68,7 @@ tar.workspace = true
|
|||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" }
|
harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" }
|
||||||
|
walkdir = "2.5.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::fs;
|
use std::fs::{self, File};
|
||||||
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -12,7 +13,8 @@ use dockerfile_builder::instruction_builder::CopyBuilder;
|
|||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use log::{debug, info, log_enabled};
|
use log::{debug, info, log_enabled};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tar::Archive;
|
use tar::{Archive, Builder, Header};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::config::{REGISTRY_PROJECT, REGISTRY_URL};
|
use crate::config::{REGISTRY_PROJECT, REGISTRY_URL};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -162,7 +164,6 @@ impl RustWebapp {
|
|||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
debug!("Generating Dockerfile for '{}'", self.name);
|
debug!("Generating Dockerfile for '{}'", self.name);
|
||||||
let dockerfile = self.get_or_build_dockerfile();
|
let dockerfile = self.get_or_build_dockerfile();
|
||||||
let docker = Docker::connect_with_socket_defaults().unwrap();
|
|
||||||
let quiet = !log_enabled!(log::Level::Debug);
|
let quiet = !log_enabled!(log::Level::Debug);
|
||||||
match dockerfile
|
match dockerfile
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -171,6 +172,14 @@ impl RustWebapp {
|
|||||||
{
|
{
|
||||||
Some(path_str) => {
|
Some(path_str) => {
|
||||||
debug!("Building from dockerfile {}", path_str);
|
debug!("Building from dockerfile {}", path_str);
|
||||||
|
|
||||||
|
let tar_data = self
|
||||||
|
.create_deterministic_tar(&self.project_root.clone())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let docker = Docker::connect_with_socket_defaults().unwrap();
|
||||||
|
|
||||||
let build_image_options =
|
let build_image_options =
|
||||||
bollard::query_parameters::BuildImageOptionsBuilder::default()
|
bollard::query_parameters::BuildImageOptionsBuilder::default()
|
||||||
.dockerfile(path_str)
|
.dockerfile(path_str)
|
||||||
@ -178,25 +187,11 @@ impl RustWebapp {
|
|||||||
.q(quiet)
|
.q(quiet)
|
||||||
.version(bollard::query_parameters::BuilderVersion::BuilderV1)
|
.version(bollard::query_parameters::BuilderVersion::BuilderV1)
|
||||||
.platform("linux/x86_64");
|
.platform("linux/x86_64");
|
||||||
let mut temp_tar_builder = tar::Builder::new(Vec::new());
|
|
||||||
temp_tar_builder
|
|
||||||
.append_dir_all("", self.project_root.clone())
|
|
||||||
.unwrap();
|
|
||||||
let archive = temp_tar_builder
|
|
||||||
.into_inner()
|
|
||||||
.expect("couldn't finish creating tar");
|
|
||||||
let archived_files = Archive::new(archive.as_slice())
|
|
||||||
.entries()
|
|
||||||
.unwrap()
|
|
||||||
.map(|entry| entry.unwrap().path().unwrap().into_owned())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
debug!("files in docker tar: {:#?}", archived_files);
|
|
||||||
|
|
||||||
let mut image_build_stream = docker.build_image(
|
let mut image_build_stream = docker.build_image(
|
||||||
build_image_options.build(),
|
build_image_options.build(),
|
||||||
None,
|
None,
|
||||||
Some(body_full(archive.into())),
|
Some(body_full(tar_data.into())),
|
||||||
);
|
);
|
||||||
|
|
||||||
while let Some(msg) = image_build_stream.next().await {
|
while let Some(msg) = image_build_stream.next().await {
|
||||||
@ -213,6 +208,56 @@ impl RustWebapp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///normalizes timestamp and ignores files that will bust the docker cach
|
||||||
|
async fn create_deterministic_tar(
|
||||||
|
&self,
|
||||||
|
project_root: &std::path::Path,
|
||||||
|
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||||
|
debug!("building tar file from project root {:#?}", project_root);
|
||||||
|
let mut tar_data = Vec::new();
|
||||||
|
{
|
||||||
|
let mut builder = Builder::new(&mut tar_data);
|
||||||
|
let ignore_prefixes = [
|
||||||
|
"target",
|
||||||
|
".git",
|
||||||
|
".github",
|
||||||
|
".harmony_generated",
|
||||||
|
"node_modules",
|
||||||
|
];
|
||||||
|
let mut entries: Vec<_> = WalkDir::new(project_root)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.filter(|e| e.file_type().is_file())
|
||||||
|
.filter(|e| {
|
||||||
|
let rel_path = e.path().strip_prefix(project_root).unwrap();
|
||||||
|
!ignore_prefixes
|
||||||
|
.iter()
|
||||||
|
.any(|prefix| rel_path.starts_with(prefix))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
entries.sort_by_key(|e| e.path().to_owned());
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
let path = entry.path();
|
||||||
|
let rel_path = path.strip_prefix(project_root).unwrap();
|
||||||
|
|
||||||
|
let mut file = fs::File::open(path)?;
|
||||||
|
let mut header = Header::new_gnu();
|
||||||
|
|
||||||
|
header.set_size(entry.metadata()?.len());
|
||||||
|
header.set_mode(0o644);
|
||||||
|
header.set_mtime(0);
|
||||||
|
header.set_uid(0);
|
||||||
|
header.set_gid(0);
|
||||||
|
|
||||||
|
builder.append_data(&mut header, rel_path, &mut file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.finish()?;
|
||||||
|
}
|
||||||
|
Ok(tar_data)
|
||||||
|
}
|
||||||
|
|
||||||
/// Tags and pushes a Docker image to the configured remote registry.
|
/// Tags and pushes a Docker image to the configured remote registry.
|
||||||
async fn push_docker_image(
|
async fn push_docker_image(
|
||||||
&self,
|
&self,
|
||||||
|
Loading…
Reference in New Issue
Block a user