Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a97a7a6
test: uncomment commented remove dir at end of tests
TomBurdge May 5, 2025
3a4eda1
chore: add anyhow to requirements
TomBurdge May 5, 2025
d464f24
refactor: use anyhow with dockpack pull
TomBurdge May 5, 2025
dc122c9
refactor: build dockerfile command with anyhow crate
TomBurdge May 5, 2025
7610e7d
refactor: make test_execute_push pass again & use anyhow crate for er…
TomBurdge May 5, 2025
58a76ad
chore: changes for clippy and rust formatting
TomBurdge May 5, 2025
84ed3da
refactor: comply with clippy lint
TomBurdge May 7, 2025
596bf82
chore: correct typo in error message
TomBurdge May 7, 2025
52e2499
Merge branch 'main' into refactor/use_anyhow_errors
TomBurdge May 28, 2025
68f454a
refactor: make C type function C FFI safe
TomBurdge Jun 28, 2025
5ac0e3e
chore: add python ignores to gitignore
TomBurdge Jun 29, 2025
e86aac8
feat: add project dependencies
TomBurdge Jun 29, 2025
ae94bc9
feat: add python binding through C API
TomBurdge Jun 29, 2025
61cb152
tests: add pull test with python
TomBurdge Jun 29, 2025
6e84e52
chore: remove commented rust test
TomBurdge Jun 29, 2025
8bf9152
refactor: move tests to tests dir (messes with the __init__.py struct…
TomBurdge Jun 29, 2025
9f9156c
refactor: python is in a dir called python like in SurrealML
TomBurdge Jun 29, 2025
39efb3d
chore: add justfile for running locally
TomBurdge Jun 29, 2025
0358ff1
revert: doesn't work with `python` directory
TomBurdge Jun 29, 2025
ac6277f
chore: gitignore lib.so/ffi binding python dir
TomBurdge Jun 29, 2025
723e282
fix: change test import dir from refactor
TomBurdge Jun 29, 2025
a1292be
feat: python interface returns directory string
TomBurdge Jun 29, 2025
438bed0
chore: remove python3 info from file
TomBurdge Jun 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Will be specific for the platform/CI...
coredockpack/dockpack_py/dockpack_py/

*/cache/*
# Generated by Cargo
# will have compiled files and executables
Expand All @@ -22,3 +25,138 @@ Cargo.lock
.idea/

.DS_Store

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
.ruff_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.venv

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# vscode project settings
.vscode
*.code-workspace
.code-workspace
debug.log

# Files
.DS_Store
poetry.lock
1 change: 1 addition & 0 deletions core-dockpack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ futures-util = "0.3.31"
tokio-util = { version = "0.7.14", features = ["io"] }
tokio-tar = "0.3.1"
futures-core = "0.3.31"
anyhow = "1.0.98"
13 changes: 10 additions & 3 deletions core-dockpack/src/cmd_processes/build/build_dockerfile.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
//! Builds a Dockerfile from a directory

use anyhow::{Context, Result};
use std::fs::File;
use std::io::Write;

// directory is the build context

pub fn create_dockerfile(directory: &str) -> Result<(), String> {
pub fn create_dockerfile(directory: &str) -> Result<()> {
let docker_file_content = "FROM scratch\nCOPY . .\n".to_string();

let dockerfile_path = format!("{}/Dockerfile", directory);

let mut dockerfile = File::create(&dockerfile_path).map_err(|e| e.to_string())?;
let mut dockerfile = File::create(&dockerfile_path)
.with_context(|| format!("Error creatining file at path {}", dockerfile_path))?;

dockerfile
.write_all(docker_file_content.as_bytes())
.map_err(|e| e.to_string())?;
.with_context(|| {
format!(
"Could not write all from scratch content to dockerfile at path {}",
dockerfile_path,
)
})?;

Ok(())
}
Expand Down
5 changes: 3 additions & 2 deletions core-dockpack/src/cmd_processes/pull/unpack_files.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! The API for unpacking Docker images into a directory.
use crate::utils::{cache, docker_commands, unpacking};
use anyhow::Result;
use std::path::PathBuf;

/// Unpacks the files from a Docker image into a directory.
Expand All @@ -10,7 +11,7 @@ use std::path::PathBuf;
///
/// # Returns
/// The path to the directory where the Docker image files are stored.
pub async fn unpack_files_from_image(image: &str, directory: &str) -> Result<String, String> {
pub async fn unpack_files_from_image(image: &str, directory: &str) -> Result<String> {
let main_path = PathBuf::from(directory);
cache::wipe_and_create_cache(&main_path);

Expand Down Expand Up @@ -46,6 +47,6 @@ mod tests {
assert!(result.is_ok());
let path = result.unwrap();
assert!(Path::new(&path).exists());
// fs::remove_dir_all(directory).unwrap();
fs::remove_dir_all(directory).unwrap();
}
}
33 changes: 17 additions & 16 deletions core-dockpack/src/cmd_processes/push/execute_push.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
use crate::utils::cache;
use anyhow::{Context, Result};
use bollard::models::{CreateImageInfo, PushImageInfo};
use bollard::query_parameters::{CreateImageOptionsBuilder, PushImageOptionsBuilder};
use bollard::Docker;
use futures_util::stream::TryStreamExt;
use tokio::fs::File;
use tokio_util::io::ReaderStream;

async fn dir_to_tar(dir: &str, image_name: &str) -> Result<(), String> {
let tar_name = &format!("{}.tar", cache::process_image_name(image_name));
let tar = File::create(tar_name)
async fn dir_to_tar(dir: &str, image_name: &str) -> Result<String> {
let tar_name = format!("{}.tar", cache::process_image_name(image_name));
let tar = File::create(&tar_name)
.await
.expect("Could not create archive file");
.with_context(|| "Could not create archive file")?;
let mut tar = tokio_tar::Builder::new(tar);
tar.append_dir_all("", dir)
.await
.expect("Could not add path to target");
.with_context(|| "Could not add path to target")?;
tar.finish()
.await
.expect("An error occured in converting dir to tar");
Ok(())
.with_context(|| "An error occured in converting dir to tar")?;
Ok(tar_name)
}

pub async fn execute_docker_build(directory: &str, image: &str) -> Result<(), String> {
pub async fn execute_docker_build(directory: &str, image: &str) -> Result<()> {
// Convert directory to a tar file
dir_to_tar(directory, image).await?;
let tar_path = dir_to_tar(directory, image).await?;

let file = File::open(&format!("{}.tar", image))
let file = File::open(tar_path)
.await
.expect("Could not find archive.");
.with_context(|| format!("Could not find archive at path {}.tar", image))?;
let stream = ReaderStream::new(file);

let docker = Docker::connect_with_socket_defaults()
.expect("Could no connect to docker socket. Is docker running?");
.with_context(|| "Could not connect to docker socket. Is docker running?")?;

let options = CreateImageOptionsBuilder::default()
.from_src("-") // from_src must be "-" when sending the archive in the request body
Expand All @@ -42,14 +43,14 @@ pub async fn execute_docker_build(directory: &str, image: &str) -> Result<(), St
.create_image(Some(options), Some(bollard::body_try_stream(stream)), None)
.try_collect()
.await
.expect("Could not create image");
.with_context(|| "Could not create image")?;

let options = PushImageOptionsBuilder::new().tag("latest").build();
let _: Vec<PushImageInfo> = docker
.push_image(&cache::process_image_name(image), Some(options), None)
.try_collect()
.await
.expect("Could not push image");
.with_context(|| "Could not push image")?;

Ok(())
}
Expand All @@ -73,7 +74,7 @@ mod tests {

assert!(result.is_ok());

// fs::remove_dir_all(directory).expect("Failed to remove test directory");
fs::remove_dir_all(directory).expect("Failed to remove test directory");
}

#[tokio::test]
Expand All @@ -89,6 +90,6 @@ mod tests {
let result = execute_docker_build(directory, image_name).await;
assert!(result.is_ok());

// fs::remove_dir_all(directory).expect("Failed to remove test directory");
fs::remove_dir_all(directory).expect("Failed to remove test directory");
}
}
34 changes: 19 additions & 15 deletions core-dockpack/src/utils/docker_commands.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
//! Defines the actions around downloading and unpacking docker images to access the files.
use super::cache::process_image_name;
use bollard::image::CreateImageOptions;
use anyhow::{anyhow, Context, Result};
use bollard::query_parameters::CreateImageOptionsBuilder;
use bollard::Docker;
use futures_util::stream::TryStreamExt;
use futures_util::StreamExt;
use std::default::Default;
use std::process::Command;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use tokio_tar::Archive;

async fn pull_image(image_name: &str, docker: &Docker) -> Result<(), String> {
let options = Some(CreateImageOptions {
from_image: image_name,
..Default::default()
});
async fn pull_image(image_name: &str, docker: &Docker) -> Result<()> {
let options = Some(
CreateImageOptionsBuilder::new()
.from_image(image_name)
.build(),
);
println!("image_name: {}", image_name);
// confirmed: this works
docker
.create_image(options, None, None)
.try_collect::<Vec<_>>()
.await
.map_err(|err| err.to_string())?;
.with_context(|| "Error creating image")?;
Ok(())
}

Expand All @@ -36,7 +37,7 @@ async fn pull_image(image_name: &str, docker: &Docker) -> Result<(), String> {
///
/// # Returns
/// The path to where the compressed Docker image files are stored
pub async fn save_docker_image(image_name: &str, tar_path: &str) -> Result<String, String> {
pub async fn save_docker_image(image_name: &str, tar_path: &str) -> Result<String> {
// pull image
// // TODO: consider moving to receive in the function
let docker = Docker::connect_with_socket_defaults()
Expand All @@ -56,17 +57,17 @@ pub async fn save_docker_image(image_name: &str, tar_path: &str) -> Result<Strin
let mut tar = docker.export_image(image_name);
let mut archive_file = File::create(file_path)
.await
.map_err(|err| err.to_string())?;
.with_context(|| "Could not create file path for tar file")?;
while let Some(chunk) = tar.next().await {
let data = chunk.map_err(|err| err.to_string())?;
let data = chunk.with_context(|| "Could not read bytes from zip stream")?;
archive_file
.write_all(&data)
.await
.map_err(|err| err.to_string())?;
.with_context(|| "Error writing bytes to file")?;
archive_file
.sync_all()
.await
.map_err(|err| err.to_string())?;
.with_context(|| "Error syncing all data to file")?;
}
println!("Synced to tar file");

Expand All @@ -75,12 +76,15 @@ pub async fn save_docker_image(image_name: &str, tar_path: &str) -> Result<Strin
.expect("Could not reopen archive file");
let mut archive = Archive::new(file);

archive.unpack(tar_path).await.map_err(|e| e.to_string())?;
archive
.unpack(tar_path)
.await
.with_context(|| "Error unpacking tar file")?;
//
// // return statement
Ok(match tar_path.to_str() {
Some(v) => v.to_string(),
None => return Err("Failed to convert path to string".to_string()),
None => return Err(anyhow!("Failed to convert path to string")),
})
}

Expand Down
Loading