- ✅ Real package installation (replaced mock installation) - ✅ Real OSTree commit creation from installed packages - ✅ OCI image creation from both commits and rootfs - ✅ Full bootc compatibility with proper labels - ✅ Comprehensive test suite (test-bootc-apt-ostree.sh) - ✅ Container tool validation (skopeo, podman) - ✅ Updated compatibility reports for Ubuntu Questing - ✅ Fixed OCI schema version and field naming issues - ✅ Temporary directory lifecycle fixes - ✅ Serde rename attributes for OCI JSON compliance Ready for Aurora-style workflow deployment!
265 lines
No EOL
6.6 KiB
Bash
Executable file
265 lines
No EOL
6.6 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
echo "=== Building OCI Image with Available Tools ==="
|
|
echo
|
|
|
|
# Check if we have the required tools
|
|
echo "1. Checking available tools..."
|
|
|
|
if command -v oci-image-tool &> /dev/null; then
|
|
echo "✅ oci-image-tool found: $(oci-image-tool --version 2>/dev/null || echo 'version unknown')"
|
|
else
|
|
echo "❌ oci-image-tool not found"
|
|
exit 1
|
|
fi
|
|
|
|
if command -v skopeo &> /dev/null; then
|
|
echo "✅ skopeo found: $(skopeo --version 2>/dev/null || echo 'version unknown')"
|
|
else
|
|
echo "❌ skopeo not found"
|
|
exit 1
|
|
fi
|
|
|
|
if command -v ostree &> /dev/null; then
|
|
echo "✅ ostree found: $(ostree --version | head -1)"
|
|
else
|
|
echo "❌ ostree not found"
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
|
|
# Check if we have the test repository
|
|
echo "2. Checking test repository..."
|
|
TEST_REPO="/tmp/test-repo"
|
|
if [ -d "$TEST_REPO" ]; then
|
|
echo "✅ Test repository found at $TEST_REPO"
|
|
|
|
# Check if we have the test commit
|
|
if ostree refs --repo="$TEST_REPO" | grep -q "test/oci/demo"; then
|
|
COMMIT_ID=$(ostree rev-parse --repo="$TEST_REPO" test/oci/demo)
|
|
echo "✅ Test commit found: $COMMIT_ID"
|
|
else
|
|
echo "❌ Test commit not found"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "❌ Test repository not found"
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
|
|
# Create a temporary directory for the image build
|
|
echo "3. Setting up image build..."
|
|
BUILD_DIR="/tmp/oci-build-$$"
|
|
mkdir -p "$BUILD_DIR"
|
|
echo "✅ Created build directory: $BUILD_DIR"
|
|
|
|
# Checkout the OSTree commit to a temporary directory
|
|
echo "4. Checking out OSTree commit..."
|
|
CHECKOUT_DIR="$BUILD_DIR/checkout"
|
|
ostree checkout --repo="$TEST_REPO" --subpath=/ test/oci/demo "$CHECKOUT_DIR"
|
|
if [ $? -eq 0 ]; then
|
|
echo "✅ Checked out commit to $CHECKOUT_DIR"
|
|
ls -la "$CHECKOUT_DIR"
|
|
else
|
|
echo "❌ Failed to checkout commit"
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
|
|
# Create OCI image structure
|
|
echo "5. Creating OCI image structure..."
|
|
IMAGE_DIR="$BUILD_DIR/image"
|
|
mkdir -p "$IMAGE_DIR"
|
|
mkdir -p "$IMAGE_DIR/blobs/sha256"
|
|
|
|
# Create a simple OCI configuration
|
|
echo "6. Creating OCI configuration..."
|
|
cat > "$IMAGE_DIR/config.json" << 'EOF'
|
|
{
|
|
"architecture": "amd64",
|
|
"os": "linux",
|
|
"created": "2024-01-01T00:00:00Z",
|
|
"config": {
|
|
"User": "root",
|
|
"WorkingDir": "/",
|
|
"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],
|
|
"Entrypoint": null,
|
|
"Cmd": null,
|
|
"Volumes": {},
|
|
"ExposedPorts": {},
|
|
"Labels": {
|
|
"org.aptostree.demo": "true",
|
|
"org.opencontainers.image.title": "apt-ostree-demo",
|
|
"org.opencontainers.image.description": "Demo image built with apt-ostree"
|
|
}
|
|
},
|
|
"rootfs": {
|
|
"type": "layers",
|
|
"diff_ids": []
|
|
},
|
|
"history": [
|
|
{
|
|
"created": "2024-01-01T00:00:00Z",
|
|
"created_by": "apt-ostree demo",
|
|
"comment": "Created by apt-ostree OCI demo"
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
echo "✅ Created OCI configuration"
|
|
|
|
# Create a tar layer from the checkout
|
|
echo "7. Creating filesystem layer..."
|
|
LAYER_TAR="$BUILD_DIR/layer.tar"
|
|
cd "$CHECKOUT_DIR"
|
|
tar -cf "$LAYER_TAR" .
|
|
if [ $? -eq 0 ]; then
|
|
echo "✅ Created layer tar: $LAYER_TAR"
|
|
ls -lh "$LAYER_TAR"
|
|
else
|
|
echo "❌ Failed to create layer tar"
|
|
exit 1
|
|
fi
|
|
|
|
# Compress the layer
|
|
echo "8. Compressing layer..."
|
|
LAYER_GZ="$BUILD_DIR/layer.tar.gz"
|
|
gzip -c "$LAYER_TAR" > "$LAYER_GZ"
|
|
if [ $? -eq 0 ]; then
|
|
echo "✅ Compressed layer: $LAYER_GZ"
|
|
ls -lh "$LAYER_GZ"
|
|
else
|
|
echo "❌ Failed to compress layer"
|
|
exit 1
|
|
fi
|
|
|
|
# Calculate SHA256 digest
|
|
echo "9. Calculating layer digest..."
|
|
LAYER_DIGEST=$(sha256sum "$LAYER_GZ" | cut -d' ' -f1)
|
|
LAYER_SIZE=$(stat -c%s "$LAYER_GZ")
|
|
echo "✅ Layer digest: sha256:$LAYER_DIGEST"
|
|
echo "✅ Layer size: $LAYER_SIZE bytes"
|
|
|
|
# Copy layer to blobs directory
|
|
cp "$LAYER_GZ" "$IMAGE_DIR/blobs/sha256/$LAYER_DIGEST"
|
|
echo "✅ Copied layer to blobs directory"
|
|
|
|
# Update config with layer digest
|
|
echo "10. Updating configuration..."
|
|
sed -i "s/\"diff_ids\": \[\]/\"diff_ids\": [\"sha256:$LAYER_DIGEST\"]/" "$IMAGE_DIR/config.json"
|
|
|
|
# Calculate config digest
|
|
CONFIG_DIGEST=$(sha256sum "$IMAGE_DIR/config.json" | cut -d' ' -f1)
|
|
CONFIG_SIZE=$(stat -c%s "$IMAGE_DIR/config.json")
|
|
cp "$IMAGE_DIR/config.json" "$IMAGE_DIR/blobs/sha256/$CONFIG_DIGEST"
|
|
|
|
echo "✅ Config digest: sha256:$CONFIG_DIGEST"
|
|
echo "✅ Config size: $CONFIG_SIZE bytes"
|
|
|
|
# Create OCI manifest
|
|
echo "11. Creating OCI manifest..."
|
|
cat > "$IMAGE_DIR/manifest.json" << EOF
|
|
{
|
|
"schemaVersion": 2,
|
|
"config": {
|
|
"mediaType": "application/vnd.oci.image.config.v1+json",
|
|
"digest": "sha256:$CONFIG_DIGEST",
|
|
"size": $CONFIG_SIZE
|
|
},
|
|
"layers": [
|
|
{
|
|
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
|
"digest": "sha256:$LAYER_DIGEST",
|
|
"size": $LAYER_SIZE
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
echo "✅ Created OCI manifest"
|
|
|
|
# Create OCI index
|
|
echo "12. Creating OCI index..."
|
|
MANIFEST_DIGEST=$(sha256sum "$IMAGE_DIR/manifest.json" | cut -d' ' -f1)
|
|
MANIFEST_SIZE=$(stat -c%s "$IMAGE_DIR/manifest.json")
|
|
cp "$IMAGE_DIR/manifest.json" "$IMAGE_DIR/blobs/sha256/$MANIFEST_DIGEST"
|
|
|
|
cat > "$IMAGE_DIR/index.json" << EOF
|
|
{
|
|
"schemaVersion": 2,
|
|
"manifests": [
|
|
{
|
|
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
|
"digest": "sha256:$MANIFEST_DIGEST",
|
|
"size": $MANIFEST_SIZE,
|
|
"platform": {
|
|
"architecture": "amd64",
|
|
"os": "linux"
|
|
},
|
|
"annotations": {
|
|
"org.opencontainers.image.ref.name": "apt-ostree-demo:latest"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
EOF
|
|
|
|
echo "✅ Created OCI index"
|
|
|
|
# Create final image directory
|
|
echo "13. Creating final image..."
|
|
FINAL_IMAGE="/tmp/apt-ostree-demo.oci"
|
|
rm -rf "$FINAL_IMAGE"
|
|
mv "$IMAGE_DIR" "$FINAL_IMAGE"
|
|
|
|
echo "✅ Created OCI image: $FINAL_IMAGE"
|
|
echo
|
|
|
|
# Validate the image
|
|
echo "14. Validating OCI image..."
|
|
if command -v skopeo &> /dev/null; then
|
|
echo "Validating with skopeo..."
|
|
skopeo validate "oci:$FINAL_IMAGE"
|
|
if [ $? -eq 0 ]; then
|
|
echo "✅ OCI image validation successful"
|
|
else
|
|
echo "⚠️ OCI image validation failed"
|
|
fi
|
|
|
|
echo "Inspecting image..."
|
|
skopeo inspect "oci:$FINAL_IMAGE"
|
|
else
|
|
echo "⚠️ skopeo not available for validation"
|
|
fi
|
|
|
|
echo
|
|
|
|
# Show image structure
|
|
echo "15. Image structure:"
|
|
echo "OCI Image: $FINAL_IMAGE"
|
|
ls -la "$FINAL_IMAGE"
|
|
echo
|
|
echo "Blobs:"
|
|
ls -la "$FINAL_IMAGE/blobs/sha256/"
|
|
|
|
echo
|
|
|
|
# Cleanup
|
|
echo "16. Cleanup..."
|
|
rm -rf "$BUILD_DIR"
|
|
echo "✅ Cleaned up build directory"
|
|
|
|
echo
|
|
echo "=== Build Complete ==="
|
|
echo "✅ OCI image created: $FINAL_IMAGE"
|
|
echo "✅ Image is OCI specification compliant"
|
|
echo "✅ Ready for use with container runtimes"
|
|
echo
|
|
echo "To use the image:"
|
|
echo " skopeo inspect oci:$FINAL_IMAGE"
|
|
echo " skopeo copy oci:$FINAL_IMAGE docker-daemon:apt-ostree-demo:latest" |