This commit is contained in:
2024-07-30 01:46:59 -04:00
commit 3496e77824
15 changed files with 191 additions and 0 deletions

26
src/dkr_functions.bash Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/bash -e
function dkr::save_and_extract() {
# image name = $1
# file name = $2
g::verbose "Extracting image $1 as $2"
docker save "$1" -o "$2.tar"
mkdir "$2"
cd "$2"
tar xf "../$2.tar"
cd - > /dev/null
rm -f "$2.tar"
}
function dkr::import_fs_as_layer() {
# tar path = $1
# base image = $2
# final image = $3
docker image import "$1" docker-isolate-flat-temp:latest > /dev/null
echo "FROM docker-isolate-flat-temp:latest as isolated" > Dockerfile
echo "FROM $2 as from_image" >> Dockerfile
echo "COPY --from=isolated / /" >> Dockerfile
docker image build -f Dockerfile -t "$3" . > /dev/null
docker image rm -f docker-isolate-flat-temp:latest > /dev/null
rm -f Dockerfile
}

82
src/isolate.bash Normal file
View File

@@ -0,0 +1,82 @@
#!/bin/bash -e
g::source dkr_functions
function app::dlt::isolate() {
local args="$1"
from_image="$(g::arg "$args" from_image)"
to_image="$(g::arg "$args" to_image)"
g::info "Isolating changes from $from_image to $to_image"
# Resolve the output path for the tarball since we're going to be changing directories
output_path="$(g::flag "$args" output)"
if [ -z "$output_path" ]; then
output_path="$(pwd)/isolated.tar"
fi
output_path="$(g::path::resolve "$output_path")"
g::verbose "Output path: $output_path"
g::path::cd "$(g::path::tmpdir)"
g::verbose "Working dir: $(pwd)"
# Extract both the base and final image in container layer format
dkr::save_and_extract "$to_image" to_image
dkr::save_and_extract "$from_image" from_image
# Load arrays of BOTH the raw layer tarballs and the SHA sums of the layers in the final image
to_json_path="$(jq -r '.[0].Config' < to_image/manifest.json)"
g::verbose "Found config for to_image: ${to_json_path}"
readarray -t to_raw_layers <<< "$(jq -r '.[0].Layers[]' < to_image/manifest.json)"
readarray -t to_sha_layers <<< "$(jq -r '.rootfs.diff_ids[]' < "to_image/${to_json_path}")"
# Load an array of the SHA sums of the layers in the base image
from_json_path="$(jq -r '.[0].Config' < from_image/manifest.json)"
g::verbose "Found config for from_image: ${from_json_path}"
readarray -t from_sha_layers <<< "$(jq -r '.rootfs.diff_ids[]' < "from_image/${from_json_path}")"
mkdir isolated
cd isolated
# Extract the isolated layers
for ((i = 0 ; i < "${#to_raw_layers[@]}" ; i++)); do
raw_layer="${to_raw_layers[$i]}"
sha_layer="${to_sha_layers[$i]}"
# If the SHA sum of this layer appears in the base image, skip it
if g::arr::includes "$sha_layer" "${from_sha_layers[@]}"; then
g::debug "Skipping layer from base image: $sha_layer"
continue
fi
# Extract the layer into the isolated directory
g::info "Unpacking layer: $sha_layer -> $raw_layer"
tar xf "../to_image/${raw_layer}"
done
# Create a tarball w/ the isolated files (special find magic to avoid the leading ./ on paths)
g::info 'Creating archive'
cd ..
find isolated/ \( -type f -o -type d \) -printf "%P\n" | tar cf isolated.tar -C isolated/ -T -
# Do a bit of cleanup
rm -rf to_image from_image isolated
# If the user asked for the isolated filesystem to be re-imported as a docker image, do that
as_image="$(g::flag "$args" as)"
if [ -n "$as_image" ]; then
if g::flag::has "$args" no-base; then
g::info "Creating image $as_image without base layers"
docker image import isolated.tar "$as_image" > /dev/null
else
g::info "Creating image $as_image on top of $from_image"
dkr::import_fs_as_layer isolated.tar "$from_image" "$as_image"
fi
fi
# Create the output archive and exit
g::verbose "Moving archive to final destination"
mv isolated.tar "$output_path"
}

7
src/setup.bash Normal file
View File

@@ -0,0 +1,7 @@
#!/bin/bash -e
SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
export PATH="${SRC_DIR}/lib/jq:${PATH}"
g::log::setLevel info