diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-19 19:07:31 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-26 01:05:01 +0000 |
| commit | 05b30f9e0a8edf59a258a891cf987287b6acb889 (patch) | |
| tree | fdc1ac630a0cbcf8e1c6800881acd9aec8691b55 | |
| parent | c118bfe7af02d22efd4f43b19dcb40fbb348d616 (diff) | |
| download | meta-virtualization-05b30f9e0a8edf59a258a891cf987287b6acb889.tar.gz | |
vcontainer: add bundle command for OCI runtime bundle creation
Add 'bundle' command to the vcontainer CLI for creating OCI runtime
bundles from container images. Pulls the image via skopeo, extracts
layers into rootfs/, resolves entrypoint/cmd/env from OCI config, and
generates config.json. Supports command override via -- separator.
Only available on the Xen (vxn) backend.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
| -rwxr-xr-x | recipes-containers/vcontainer/files/vcontainer-common.sh | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/recipes-containers/vcontainer/files/vcontainer-common.sh b/recipes-containers/vcontainer/files/vcontainer-common.sh index 7f3b8945..ea78b265 100755 --- a/recipes-containers/vcontainer/files/vcontainer-common.sh +++ b/recipes-containers/vcontainer/files/vcontainer-common.sh | |||
| @@ -539,6 +539,7 @@ ${BOLD}${RUNTIME_UPPER}-COMPATIBLE COMMANDS:${NC} | |||
| 539 | ${BOLD}EXTENDED COMMANDS (${VCONTAINER_RUNTIME_NAME}-specific):${NC} | 539 | ${BOLD}EXTENDED COMMANDS (${VCONTAINER_RUNTIME_NAME}-specific):${NC} |
| 540 | ${CYAN}vimport${NC} <path> [name:tag] Import from OCI dir, tarball, or directory (auto-detect) | 540 | ${CYAN}vimport${NC} <path> [name:tag] Import from OCI dir, tarball, or directory (auto-detect) |
| 541 | Multi-arch OCI Image Index supported (auto-selects platform) | 541 | Multi-arch OCI Image Index supported (auto-selects platform) |
| 542 | ${CYAN}bundle${NC} <image> <dir> [-- cmd] Create OCI bundle from image (for vxn-oci-runtime) | ||
| 542 | ${CYAN}vrun${NC} [opts] <image> [cmd] Run command, clearing entrypoint (see RUN vs VRUN below) | 543 | ${CYAN}vrun${NC} [opts] <image> [cmd] Run command, clearing entrypoint (see RUN vs VRUN below) |
| 543 | ${CYAN}vstorage${NC} List all storage directories (alias: vstorage list) | 544 | ${CYAN}vstorage${NC} List all storage directories (alias: vstorage list) |
| 544 | ${CYAN}vstorage list${NC} List all storage directories with details | 545 | ${CYAN}vstorage list${NC} List all storage directories with details |
| @@ -3083,6 +3084,123 @@ case "$COMMAND" in | |||
| 3083 | esac | 3084 | esac |
| 3084 | ;; | 3085 | ;; |
| 3085 | 3086 | ||
| 3087 | bundle) | ||
| 3088 | # Create an OCI runtime bundle from a container image. | ||
| 3089 | # Usage: <tool> bundle <image> <output-dir> [-- <cmd> ...] | ||
| 3090 | # | ||
| 3091 | # Pulls the image via skopeo, extracts layers into rootfs/, | ||
| 3092 | # and generates config.json from the OCI image config. | ||
| 3093 | # Optional command after -- overrides the image's default entrypoint. | ||
| 3094 | # The resulting bundle can be passed to vxn-oci-runtime create --bundle. | ||
| 3095 | [ "${VCONTAINER_HYPERVISOR:-}" = "xen" ] || { | ||
| 3096 | echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} bundle is only supported for Xen (vxn)" >&2 | ||
| 3097 | exit 1 | ||
| 3098 | } | ||
| 3099 | |||
| 3100 | if [ ${#COMMAND_ARGS[@]} -lt 2 ]; then | ||
| 3101 | echo "Usage: $VCONTAINER_RUNTIME_NAME bundle <image> <output-dir> [-- <cmd> ...]" >&2 | ||
| 3102 | echo "" >&2 | ||
| 3103 | echo "Creates an OCI runtime bundle from a container image." >&2 | ||
| 3104 | echo "The bundle can then be used with vxn-oci-runtime:" >&2 | ||
| 3105 | echo "" >&2 | ||
| 3106 | echo " $VCONTAINER_RUNTIME_NAME bundle alpine /tmp/test-bundle -- /bin/echo hello" >&2 | ||
| 3107 | echo " vxn-oci-runtime create --bundle /tmp/test-bundle --pid-file /tmp/t.pid test1" >&2 | ||
| 3108 | echo " vxn-oci-runtime start test1" >&2 | ||
| 3109 | echo " vxn-oci-runtime state test1" >&2 | ||
| 3110 | echo " vxn-oci-runtime delete test1" >&2 | ||
| 3111 | exit 1 | ||
| 3112 | fi | ||
| 3113 | |||
| 3114 | BUNDLE_IMAGE="${COMMAND_ARGS[0]}" | ||
| 3115 | BUNDLE_DIR="${COMMAND_ARGS[1]}" | ||
| 3116 | |||
| 3117 | # Parse optional command override after -- | ||
| 3118 | BUNDLE_CMD_OVERRIDE=() | ||
| 3119 | _bundle_found_sep=false | ||
| 3120 | for _ba in "${COMMAND_ARGS[@]:2}"; do | ||
| 3121 | if [ "$_bundle_found_sep" = "true" ]; then | ||
| 3122 | BUNDLE_CMD_OVERRIDE+=("$_ba") | ||
| 3123 | elif [ "$_ba" = "--" ]; then | ||
| 3124 | _bundle_found_sep=true | ||
| 3125 | fi | ||
| 3126 | done | ||
| 3127 | |||
| 3128 | # Check prerequisites | ||
| 3129 | command -v skopeo >/dev/null 2>&1 || { | ||
| 3130 | echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} skopeo not found (needed for image pull)" >&2 | ||
| 3131 | exit 1 | ||
| 3132 | } | ||
| 3133 | command -v jq >/dev/null 2>&1 || { | ||
| 3134 | echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} jq not found (needed for OCI config parsing)" >&2 | ||
| 3135 | exit 1 | ||
| 3136 | } | ||
| 3137 | |||
| 3138 | # Pull image via skopeo | ||
| 3139 | BUNDLE_TMP=$(mktemp -d) | ||
| 3140 | trap 'rm -rf "$BUNDLE_TMP"' EXIT | ||
| 3141 | BUNDLE_OCI="$BUNDLE_TMP/oci" | ||
| 3142 | |||
| 3143 | echo -e "${CYAN}[$VCONTAINER_RUNTIME_NAME]${NC} Pulling $BUNDLE_IMAGE..." | ||
| 3144 | if ! skopeo copy "docker://$BUNDLE_IMAGE" "oci:$BUNDLE_OCI:latest" 2>&1; then | ||
| 3145 | echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} Failed to pull image: $BUNDLE_IMAGE" >&2 | ||
| 3146 | exit 1 | ||
| 3147 | fi | ||
| 3148 | |||
| 3149 | # Extract layers into rootfs/ | ||
| 3150 | mkdir -p "$BUNDLE_DIR/rootfs" | ||
| 3151 | |||
| 3152 | BUNDLE_MANIFEST_DIGEST=$(jq -r '.manifests[0].digest' "$BUNDLE_OCI/index.json") | ||
| 3153 | BUNDLE_MANIFEST="$BUNDLE_OCI/blobs/${BUNDLE_MANIFEST_DIGEST/://}" | ||
| 3154 | BUNDLE_CONFIG_DIGEST=$(jq -r '.config.digest' "$BUNDLE_MANIFEST") | ||
| 3155 | BUNDLE_CONFIG="$BUNDLE_OCI/blobs/${BUNDLE_CONFIG_DIGEST/://}" | ||
| 3156 | |||
| 3157 | echo -e "${CYAN}[$VCONTAINER_RUNTIME_NAME]${NC} Extracting layers..." | ||
| 3158 | for layer_digest in $(jq -r '.layers[].digest' "$BUNDLE_MANIFEST"); do | ||
| 3159 | layer_file="$BUNDLE_OCI/blobs/${layer_digest/://}" | ||
| 3160 | [ -f "$layer_file" ] && tar -xf "$layer_file" -C "$BUNDLE_DIR/rootfs" 2>/dev/null || true | ||
| 3161 | done | ||
| 3162 | |||
| 3163 | # Parse OCI config | ||
| 3164 | BUNDLE_ENTRYPOINT=$(jq -r '(.config.Entrypoint // [])' "$BUNDLE_CONFIG") | ||
| 3165 | BUNDLE_CMD=$(jq -r '(.config.Cmd // [])' "$BUNDLE_CONFIG") | ||
| 3166 | BUNDLE_ENV=$(jq -r '(.config.Env // [])' "$BUNDLE_CONFIG") | ||
| 3167 | BUNDLE_CWD=$(jq -r '.config.WorkingDir // "/"' "$BUNDLE_CONFIG") | ||
| 3168 | [ -z "$BUNDLE_CWD" ] && BUNDLE_CWD="/" | ||
| 3169 | |||
| 3170 | # Merge Entrypoint + Cmd into process.args (or use override) | ||
| 3171 | if [ ${#BUNDLE_CMD_OVERRIDE[@]} -gt 0 ]; then | ||
| 3172 | BUNDLE_ARGS=$(printf '%s\n' "${BUNDLE_CMD_OVERRIDE[@]}" | jq -R . | jq -s .) | ||
| 3173 | else | ||
| 3174 | BUNDLE_ARGS=$(jq -n \ | ||
| 3175 | --argjson ep "$BUNDLE_ENTRYPOINT" \ | ||
| 3176 | --argjson cmd "$BUNDLE_CMD" \ | ||
| 3177 | '$ep + $cmd') | ||
| 3178 | fi | ||
| 3179 | |||
| 3180 | # Generate config.json | ||
| 3181 | jq -n \ | ||
| 3182 | --argjson args "$BUNDLE_ARGS" \ | ||
| 3183 | --argjson env "$BUNDLE_ENV" \ | ||
| 3184 | --arg cwd "$BUNDLE_CWD" \ | ||
| 3185 | '{ | ||
| 3186 | ociVersion: "1.0.2", | ||
| 3187 | process: { | ||
| 3188 | args: $args, | ||
| 3189 | env: $env, | ||
| 3190 | cwd: $cwd | ||
| 3191 | }, | ||
| 3192 | root: { path: "rootfs" } | ||
| 3193 | }' > "$BUNDLE_DIR/config.json" | ||
| 3194 | |||
| 3195 | rm -rf "$BUNDLE_TMP" | ||
| 3196 | trap - EXIT | ||
| 3197 | |||
| 3198 | BUNDLE_ARGS_DISPLAY=$(jq -r 'join(" ")' <<< "$BUNDLE_ARGS") | ||
| 3199 | echo -e "${GREEN}[$VCONTAINER_RUNTIME_NAME]${NC} Bundle created: $BUNDLE_DIR" | ||
| 3200 | echo -e " entrypoint: $BUNDLE_ARGS_DISPLAY" | ||
| 3201 | echo -e " cwd: $BUNDLE_CWD" | ||
| 3202 | ;; | ||
| 3203 | |||
| 3086 | *) | 3204 | *) |
| 3087 | echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} Unknown command: $COMMAND" >&2 | 3205 | echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} Unknown command: $COMMAND" >&2 |
| 3088 | echo "Run '$VCONTAINER_RUNTIME_NAME --help' for usage" >&2 | 3206 | echo "Run '$VCONTAINER_RUNTIME_NAME --help' for usage" >&2 |
