Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix bug commit is not checking if same name exist + image created and size to cmd output #21

Merged
merged 2 commits into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions examples/build_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
SCRIPT_DIR=$(dirname `realpath $0`)
OCIBUILDER="${SCRIPT_DIR}/../bin/ocibuilder"
if [ ! -f $OCIBUILDER ] ; then
echo "cannot locate ocibuilder bin: $OCIBUILDER"
exit 1
fi

cntr=$($OCIBUILDER from quay.io/quay/busybox:latest | tail -1)
$OCIBUILDER config --author navid --working-dir /tmp $cntr
$OCIBUILDER config --label "owner=ocibuilder" $cntr
$OCIBUILDER run $cntr touch ocibuilder.txt
$OCIBUILDER commit $cntr quay.io/ocibuilder/ocibuilder-test:latest
[ -f "/tmp/ocibuilder_new_image.tar" ] && /bin/rm -rf /tmp/ocibuilder_new_image.tar
$OCIBUILDER save -o /tmp/ocibuilder_new_image.tar quay.io/ocibuilder/ocibuilder-test:latest

exit 0
43 changes: 41 additions & 2 deletions src/builder/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,34 @@ impl OCIBuilder {
pub fn commit(&self, container: &str, name: Option<String>) -> BuilderResult<digest::Digest> {
self.lock()?;

// check image with same name exists
let new_commit_image_name = name.clone().unwrap_or_default();

if !new_commit_image_name.is_empty() {
let new_commit_image_ref: Reference = match new_commit_image_name.parse() {
Ok(img_ref) => img_ref,
Err(err) => {
return Err(BuilderError::InvalidImageName(
new_commit_image_name.to_string(),
err,
))
}
};

debug!("image reference: {:?}", new_commit_image_ref);

match self.image_store().image_digest(&new_commit_image_name) {
Ok(dg) => {
debug!("image reference digest: {:?}", dg);

if !dg.encoded.is_empty() {
return Err(BuilderError::ImageWithSameName(new_commit_image_name));
}
}
Err(_err) => {}
}
}

let cnt = self.container_store().container_exist(container)?;
let cnt_id = self.container_store().container_digest(container)?;
let top_layer = cnt.top_layer();
Expand Down Expand Up @@ -124,8 +152,17 @@ impl OCIBuilder {
let new_image_reference = self.new_image_reference(name, &new_image_id_digest)?;

// overlay-images 6- update images.json
self.image_store()
.write_images(&new_image_reference, &new_image_id_digest)?;
let mut image_size = self.calculate_image_layers_size(new_image_manifest.layers)?;
image_size += new_image_manifest.config.size;

let image_config = self.image_store().get_config(&new_image_id_digest)?;

self.image_store().write_images(
&new_image_reference,
&new_image_id_digest,
&image_size,
&image_config.created.unwrap_or_default(),
)?;

// remove tmp content
if layer_archive_path.is_file() {
Expand Down Expand Up @@ -165,6 +202,8 @@ impl OCIBuilder {
layer_type = manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE.to_string()
} else if layer.media_type == manifest::IMAGE_DOCKER_LAYER_TAR_MEDIA_TYPE {
layer_type = manifest::IMAGE_LAYER_MEDIA_TYPE.to_string()
} else if layer.media_type == manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE {
layer_type = manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE.to_string()
} else {
warn!("unknown layer type: {}", layer.media_type);
}
Expand Down
1 change: 1 addition & 0 deletions src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pub mod run;
mod run_yuki;
mod run_yuki_executer;
pub mod save;
mod utils;
11 changes: 10 additions & 1 deletion src/builder/mount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,22 @@ impl OCIBuilder {
self.umount(container)?;
}

let mount_options = format!(
let mut mount_options = format!(
"lowerdir={},upperdir={},workdir={}",
lowerdir_paths.join(":"),
upperdir_path.display(),
workdir_path.display(),
);

if lowerdir_paths.is_empty() {
mount_options = format!(
"lowerdir={},upperdir={},workdir={}",
upperdir_path.display(),
upperdir_path.display(),
workdir_path.display(),
);
}

debug!(
"container {:.12} mount options: {:?}",
cnt.id(),
Expand Down
24 changes: 17 additions & 7 deletions src/builder/pull.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ impl OCIBuilder {

let mut pull_handlers: Vec<tokio::task::JoinHandle<Result<(), BuilderError>>> = Vec::new();
let mut threads = vec![];
let mut layer_oci_manifest = manifest.clone();
let mut layer_oci_disctriptor = Vec::new();
let mut image_oci_manifest = manifest.clone();
let mut image_oci_disctriptor = Vec::new();

for layer in &manifest.layers {
layer_oci_disctriptor.push(manifest::OciDescriptor {
image_oci_disctriptor.push(manifest::OciDescriptor {
media_type: manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE.to_string(),
digest: layer.digest.clone(),
size: layer.size,
Expand Down Expand Up @@ -136,9 +136,9 @@ impl OCIBuilder {
}
}

layer_oci_manifest.layers = layer_oci_disctriptor;
image_oci_manifest.layers = image_oci_disctriptor;

for layer in &layer_oci_manifest.layers {
for layer in &image_oci_manifest.layers {
debug!("adding layers to layerstore");

self.layer_store().add_layer_desc(layer)?;
Expand All @@ -151,10 +151,20 @@ impl OCIBuilder {
// write image manifest
println!("Writing manifest to image destination");
self.image_store()
.write_manifest(&image_digest, &layer_oci_manifest)?;
.write_manifest(&image_digest, &image_oci_manifest)?;

// update images
self.image_store().write_images(&reference, &image_digest)?;
let mut image_size = self.calculate_image_layers_size(image_oci_manifest.layers)?;
image_size += image_oci_manifest.config.size;

let image_config = self.image_store().get_config(&image_digest)?;

self.image_store().write_images(
&reference,
&image_digest,
&image_size,
&image_config.created.unwrap_or_default(),
)?;

self.unlock()?;

Expand Down
50 changes: 50 additions & 0 deletions src/builder/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::{fs, io, path::PathBuf};

use crate::{
error::{BuilderError, BuilderResult},
utils,
};

use log::debug;
use oci_client::manifest::OciDescriptor;

use super::oci::OCIBuilder;

impl OCIBuilder {
pub fn calculate_image_layers_size(&self, layers: Vec<OciDescriptor>) -> BuilderResult<i64> {
let mut est_layers_size: u64 = 0;

for layer in layers {
let layer_id = utils::digest::Digest::new(&layer.digest)?;
let layer_id_path = self.layer_store().overlay_dir_path(&layer_id);

let layer_size = match dir_size(&layer_id_path) {
Ok(s) => s,
Err(err) => return Err(BuilderError::AnyError(err.to_string())),
};

debug!("dir {:?} size: {}", layer_id_path, layer_size);

est_layers_size += layer_size
}

let layers_dir_size = i64::from_ne_bytes(est_layers_size.to_ne_bytes());

Ok(layers_dir_size)
}
}

fn dir_size(path: impl Into<PathBuf>) -> io::Result<u64> {
fn dir_size(mut dir: fs::ReadDir) -> io::Result<u64> {
dir.try_fold(0, |acc, file| {
let file = file?;
let size = match file.metadata()? {
data if data.is_dir() => dir_size(fs::read_dir(file.path())?)?,
data => data.len(),
};
Ok(acc + size)
})
}

dir_size(fs::read_dir(path.into())?)
}
30 changes: 26 additions & 4 deletions src/commands/images.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::ffi::OsString;
use std::io::Write;
use std::{ffi::OsString, time::Duration};

use chrono::Local;
use clap::Parser;
use indicatif::{DecimalBytes, HumanDuration};
use oci_client::Reference;
use tabwriter::TabWriter;

Expand Down Expand Up @@ -38,19 +40,39 @@ impl Images {
}

let mut tw = TabWriter::new(std::io::stdout());
let mut output = "REPOSITORY\tTAG\tIMAGE ID\n".to_string();
let mut output = "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE\n".to_string();

for img in images {
let image_size = DecimalBytes(u64::try_from(img.size()).unwrap_or_default());
let created_time: chrono::DateTime<chrono::Utc> = img.created().to_utc();
let current_time = Local::now();
let diff_time = current_time.signed_duration_since(created_time);
let diff_time_seconds = u64::from_ne_bytes(diff_time.num_seconds().to_ne_bytes());
let image_created = HumanDuration(Duration::new(diff_time_seconds, 0));
if img.repository() == "/" && img.tag().is_empty() {
output = format!("{}<none>\t<none>\t{:.12}\n", output, img.id());
output = format!(
"{}<none>\t<none>\t{:.12}\t{} ago\t{}\n",
output,
img.id(),
image_created,
image_size,
);
} else {
let img_ref = format!("{}:{}", img.repository(), img.tag());
let reference: Reference = match img_ref.parse() {
Ok(img_ref) => img_ref,
Err(err) => return Err(BuilderError::InvalidImageName(img_ref, err)),
};
let img_name = format!("{}/{}", reference.registry(), reference.repository());
output = format!("{}{}\t{}\t{:.12}\n", output, img_name, img.tag(), img.id());
output = format!(
"{}{}\t{}\t{:.12}\t{} ago\t{}\n",
output,
img_name,
img.tag(),
img.id(),
image_created,
image_size,
);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ pub enum BuilderError {
#[error("invalid image name {0}: {1}")]
InvalidImageName(String, ParseError),

#[error("Image with same name found: {0}")]
ImageWithSameName(String),

#[error("invalid image reference: {0}")]
InvalidImageReference(String),

Expand Down
21 changes: 20 additions & 1 deletion src/image/images.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{fs::File, path::PathBuf};

use chrono::{DateTime, Utc};
use log::debug;
use oci_client::Reference;
use serde::{Deserialize, Serialize};
Expand All @@ -19,6 +20,8 @@ pub struct Image {
repository: String,
tag: String,
id: String,
size: i64,
created: DateTime<Utc>,
}

impl Image {
Expand All @@ -33,6 +36,14 @@ impl Image {
pub fn id(&self) -> String {
self.id.clone()
}

pub fn size(&self) -> i64 {
self.size
}

pub fn created(&self) -> DateTime<Utc> {
self.created
}
}

impl ImageStore {
Expand Down Expand Up @@ -107,7 +118,13 @@ impl ImageStore {
Err(BuilderError::ImageNotFound(img_digest.to_string()))
}

pub fn write_images(&self, img_ref: &Reference, dg: &digest::Digest) -> BuilderResult<()> {
pub fn write_images(
&self,
img_ref: &Reference,
dg: &digest::Digest,
size: &i64,
created: &DateTime<Utc>,
) -> BuilderResult<()> {
debug!("write images: {}", dg);

let mut images = self.images()?;
Expand All @@ -118,6 +135,8 @@ impl ImageStore {
repository: img_repo,
tag: img_ref.tag().unwrap_or_default().to_string(),
id: dg.encoded.to_owned(),
size: size.to_owned(),
created: created.to_owned(),
});

let images_path = self.images_path();
Expand Down
Loading