summaryrefslogtreecommitdiffstats
path: root/recipes-containers/vcontainer
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-01-12 16:13:45 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-09 03:32:52 +0000
commitf865a238a564e4d8a8c522ed560e089bc2f69f99 (patch)
tree3a3db72589952b300d5e6abd76d4d0bd3ec4bb21 /recipes-containers/vcontainer
parent87ed625c043e4cdbabf569227b189823cd08db8e (diff)
downloadmeta-virtualization-f865a238a564e4d8a8c522ed560e089bc2f69f99.tar.gz
vdkr: add registry configuration and pull fallback
Add registry support to vdkr: - vconfig registry command for persistent config - --registry flag for one-off usage - Registry-first, Docker Hub fallback for pulls - Baked-in registry config via CONTAINER_REGISTRY_URL - Image commands (inspect, history, rmi, images) work without transform Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/vcontainer')
-rwxr-xr-xrecipes-containers/vcontainer/files/vcontainer-common.sh95
-rwxr-xr-xrecipes-containers/vcontainer/files/vcontainer-init-common.sh26
-rwxr-xr-xrecipes-containers/vcontainer/files/vdkr-init.sh298
-rwxr-xr-xrecipes-containers/vcontainer/files/vrunner.sh31
-rw-r--r--recipes-containers/vcontainer/vdkr-rootfs-image.bb48
5 files changed, 493 insertions, 5 deletions
diff --git a/recipes-containers/vcontainer/files/vcontainer-common.sh b/recipes-containers/vcontainer/files/vcontainer-common.sh
index b0623164..f10243f1 100755
--- a/recipes-containers/vcontainer/files/vcontainer-common.sh
+++ b/recipes-containers/vcontainer/files/vcontainer-common.sh
@@ -134,6 +134,7 @@ config_default() {
134 verbose) echo "false" ;; 134 verbose) echo "false" ;;
135 idle-timeout) echo "1800" ;; # 30 minutes 135 idle-timeout) echo "1800" ;; # 30 minutes
136 auto-daemon) echo "true" ;; # Auto-start daemon by default 136 auto-daemon) echo "true" ;; # Auto-start daemon by default
137 registry) echo "" ;; # Default registry for unqualified images
137 *) echo "" ;; 138 *) echo "" ;;
138 esac 139 esac
139} 140}
@@ -404,11 +405,12 @@ ${BOLD}CONFIGURATION (vconfig):${NC}
404 ${CYAN}vconfig${NC} <key> <value> Set configuration value 405 ${CYAN}vconfig${NC} <key> <value> Set configuration value
405 ${CYAN}vconfig${NC} <key> --reset Reset to default value 406 ${CYAN}vconfig${NC} <key> --reset Reset to default value
406 407
407 Supported keys: arch, timeout, state-dir, verbose, idle-timeout, auto-daemon 408 Supported keys: arch, timeout, state-dir, verbose, idle-timeout, auto-daemon, registry
408 Config file: \$CONFIG_DIR/config (default: ~/.config/${VCONTAINER_RUNTIME_NAME}/config) 409 Config file: \$CONFIG_DIR/config (default: ~/.config/${VCONTAINER_RUNTIME_NAME}/config)
409 410
410 idle-timeout: Daemon idle timeout in seconds [default: 1800] 411 idle-timeout: Daemon idle timeout in seconds [default: 1800]
411 auto-daemon: Auto-start daemon on first command [default: true] 412 auto-daemon: Auto-start daemon on first command [default: true]
413 registry: Default registry for unqualified images (e.g., 10.0.2.2:5000/yocto)
412 414
413${BOLD}GLOBAL OPTIONS:${NC} 415${BOLD}GLOBAL OPTIONS:${NC}
414 --arch, -a <arch> Target architecture: x86_64 or aarch64 [default: ${DEFAULT_ARCH}] 416 --arch, -a <arch> Target architecture: x86_64 or aarch64 [default: ${DEFAULT_ARCH}]
@@ -421,6 +423,8 @@ ${BOLD}GLOBAL OPTIONS:${NC}
421 --input-storage <tar> Load ${RUNTIME_UPPER} state from tar before command 423 --input-storage <tar> Load ${RUNTIME_UPPER} state from tar before command
422 --no-kvm Disable KVM acceleration (use TCG emulation) 424 --no-kvm Disable KVM acceleration (use TCG emulation)
423 --no-daemon Run in ephemeral mode (don't auto-start/use daemon) 425 --no-daemon Run in ephemeral mode (don't auto-start/use daemon)
426 --registry <url> Default registry for unqualified images (e.g., 10.0.2.2:5000/yocto)
427 --insecure-registry <host:port> Mark registry as insecure (HTTP). Can repeat.
424 --verbose, -v Enable verbose output 428 --verbose, -v Enable verbose output
425 --help, -h Show this help 429 --help, -h Show this help
426 430
@@ -467,6 +471,13 @@ ${BOLD}EXAMPLES:${NC}
467 # Pull an image from a registry 471 # Pull an image from a registry
468 ${PROG_NAME} pull alpine:latest 472 ${PROG_NAME} pull alpine:latest
469 473
474 # Pull from local registry (configure once, use everywhere)
475 ${PROG_NAME} vconfig registry 10.0.2.2:5000/yocto # Set default registry
476 ${PROG_NAME} pull container-base # Pulls from 10.0.2.2:5000/yocto/container-base
477
478 # Or use --registry for one-off pulls
479 ${PROG_NAME} --registry 10.0.2.2:5000/yocto pull container-base
480
470 # vrun: convenience wrapper (clears entrypoint when command given) 481 # vrun: convenience wrapper (clears entrypoint when command given)
471 ${PROG_NAME} vrun myapp:latest /bin/ls -la # Runs /bin/ls directly, not via entrypoint 482 ${PROG_NAME} vrun myapp:latest /bin/ls -la # Runs /bin/ls directly, not via entrypoint
472 483
@@ -538,6 +549,12 @@ build_runner_args() {
538 args+=("--port-forward" "$pf") 549 args+=("--port-forward" "$pf")
539 done 550 done
540 551
552 # Add registry configuration
553 [ -n "$REGISTRY" ] && args+=("--registry" "$REGISTRY")
554 for reg in "${INSECURE_REGISTRIES[@]}"; do
555 args+=("--insecure-registry" "$reg")
556 done
557
541 echo "${args[@]}" 558 echo "${args[@]}"
542} 559}
543 560
@@ -551,6 +568,8 @@ INTERACTIVE="false"
551PORT_FORWARDS=() 568PORT_FORWARDS=()
552DISABLE_KVM="false" 569DISABLE_KVM="false"
553NO_DAEMON="false" 570NO_DAEMON="false"
571REGISTRY=""
572INSECURE_REGISTRIES=()
554COMMAND="" 573COMMAND=""
555COMMAND_ARGS=() 574COMMAND_ARGS=()
556 575
@@ -611,6 +630,14 @@ while [ $# -gt 0 ]; do
611 NO_DAEMON="true" 630 NO_DAEMON="true"
612 shift 631 shift
613 ;; 632 ;;
633 --registry)
634 REGISTRY="$2"
635 shift 2
636 ;;
637 --insecure-registry)
638 INSECURE_REGISTRIES+=("$2")
639 shift 2
640 ;;
614 -it|--interactive) 641 -it|--interactive)
615 INTERACTIVE="true" 642 INTERACTIVE="true"
616 shift 643 shift
@@ -683,6 +710,11 @@ if [ "$STATELESS" != "true" ] && [ -z "$STATE_DIR" ] && [ -z "$INPUT_STORAGE" ];
683 STATE_DIR="$DEFAULT_STATE_DIR/$TARGET_ARCH" 710 STATE_DIR="$DEFAULT_STATE_DIR/$TARGET_ARCH"
684fi 711fi
685 712
713# Read registry from config if not set via CLI
714if [ -z "$REGISTRY" ]; then
715 REGISTRY=$(config_get "registry" "")
716fi
717
686# Check runner exists 718# Check runner exists
687if [ ! -x "$RUNNER" ]; then 719if [ ! -x "$RUNNER" ]; then
688 echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} Runner script not found: $RUNNER" >&2 720 echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} Runner script not found: $RUNNER" >&2
@@ -1122,6 +1154,65 @@ parse_and_prepare_volumes() {
1122 1154
1123# Handle commands 1155# Handle commands
1124case "$COMMAND" in 1156case "$COMMAND" in
1157 image)
1158 # Handle "docker image *" compound commands
1159 # docker image ls → docker images
1160 # docker image rm → docker rmi
1161 # docker image pull → docker pull
1162 # etc.
1163 if [ ${#COMMAND_ARGS[@]} -lt 1 ]; then
1164 echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} image requires a subcommand (ls, rm, pull, inspect, tag, push, prune)" >&2
1165 exit 1
1166 fi
1167 SUBCMD="${COMMAND_ARGS[0]}"
1168 SUBCMD_ARGS=("${COMMAND_ARGS[@]:1}")
1169 case "$SUBCMD" in
1170 ls|list)
1171 run_runtime_command "$VCONTAINER_RUNTIME_CMD images ${SUBCMD_ARGS[*]}"
1172 ;;
1173 rm|remove)
1174 run_runtime_command "$VCONTAINER_RUNTIME_CMD rmi ${SUBCMD_ARGS[*]}"
1175 ;;
1176 pull)
1177 # Reuse pull logic - set COMMAND_ARGS and fall through
1178 COMMAND_ARGS=("${SUBCMD_ARGS[@]}")
1179 if [ ${#COMMAND_ARGS[@]} -lt 1 ]; then
1180 echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} image pull requires <image>" >&2
1181 exit 1
1182 fi
1183 IMAGE_NAME="${COMMAND_ARGS[0]}"
1184 if daemon_is_running; then
1185 run_runtime_command "$VCONTAINER_RUNTIME_CMD pull $IMAGE_NAME && $VCONTAINER_RUNTIME_CMD images"
1186 else
1187 NETWORK="true"
1188 RUNNER_ARGS=$(build_runner_args)
1189 "$RUNNER" $RUNNER_ARGS -- "$VCONTAINER_RUNTIME_CMD pull $IMAGE_NAME && $VCONTAINER_RUNTIME_CMD images"
1190 fi
1191 ;;
1192 inspect)
1193 run_runtime_command "$VCONTAINER_RUNTIME_CMD inspect ${SUBCMD_ARGS[*]}"
1194 ;;
1195 tag)
1196 run_runtime_command "$VCONTAINER_RUNTIME_CMD tag ${SUBCMD_ARGS[*]}"
1197 ;;
1198 push)
1199 NETWORK="true"
1200 run_runtime_command "$VCONTAINER_RUNTIME_CMD push ${SUBCMD_ARGS[*]}"
1201 ;;
1202 prune)
1203 run_runtime_command "$VCONTAINER_RUNTIME_CMD image prune ${SUBCMD_ARGS[*]}"
1204 ;;
1205 history)
1206 run_runtime_command "$VCONTAINER_RUNTIME_CMD history ${SUBCMD_ARGS[*]}"
1207 ;;
1208 *)
1209 echo -e "${RED}[$VCONTAINER_RUNTIME_NAME]${NC} Unknown image subcommand: $SUBCMD" >&2
1210 echo -e "${YELLOW}[$VCONTAINER_RUNTIME_NAME]${NC} Valid subcommands: ls, rm, pull, inspect, tag, push, prune, history" >&2
1211 exit 1
1212 ;;
1213 esac
1214 ;;
1215
1125 images) 1216 images)
1126 # runtime images 1217 # runtime images
1127 run_runtime_command "$VCONTAINER_RUNTIME_CMD images ${COMMAND_ARGS[*]}" 1218 run_runtime_command "$VCONTAINER_RUNTIME_CMD images ${COMMAND_ARGS[*]}"
@@ -1542,7 +1633,7 @@ case "$COMMAND" in
1542 1633
1543 vconfig) 1634 vconfig)
1544 # Configuration management (runs on host, not in VM) 1635 # Configuration management (runs on host, not in VM)
1545 VALID_KEYS="arch timeout state-dir verbose idle-timeout auto-daemon" 1636 VALID_KEYS="arch timeout state-dir verbose idle-timeout auto-daemon registry"
1546 1637
1547 if [ ${#COMMAND_ARGS[@]} -lt 1 ]; then 1638 if [ ${#COMMAND_ARGS[@]} -lt 1 ]; then
1548 # Show all config 1639 # Show all config
diff --git a/recipes-containers/vcontainer/files/vcontainer-init-common.sh b/recipes-containers/vcontainer/files/vcontainer-init-common.sh
index 21bbe9db..738d0343 100755
--- a/recipes-containers/vcontainer/files/vcontainer-init-common.sh
+++ b/recipes-containers/vcontainer/files/vcontainer-init-common.sh
@@ -380,6 +380,25 @@ run_daemon_mode() {
380 log "Command needs input from shared directory" 380 log "Command needs input from shared directory"
381 fi 381 fi
382 382
383 # Check if this is a pull command that needs fallback handling
384 # (try registry first, fall back to Docker Hub)
385 USE_PULL_FALLBACK=false
386 if type is_pull_command >/dev/null 2>&1 && type execute_pull_with_fallback >/dev/null 2>&1; then
387 if is_pull_command "$CMD"; then
388 USE_PULL_FALLBACK=true
389 log "Using pull with registry fallback"
390 fi
391 fi
392
393 # Transform command if runtime provides a transform function
394 # (e.g., vdkr transforms unqualified images to use default registry)
395 # Note: Pull commands are NOT transformed - they use fallback logic
396 if [ "$USE_PULL_FALLBACK" != "true" ]; then
397 if type transform_docker_command >/dev/null 2>&1 && [ -n "$DOCKER_DEFAULT_REGISTRY" ]; then
398 CMD=$(transform_docker_command "$CMD")
399 fi
400 fi
401
383 log "Executing: $CMD" 402 log "Executing: $CMD"
384 403
385 # Verify shared directory has content if needed 404 # Verify shared directory has content if needed
@@ -403,7 +422,12 @@ run_daemon_mode() {
403 # Execute command 422 # Execute command
404 EXEC_OUTPUT="/tmp/daemon_output.txt" 423 EXEC_OUTPUT="/tmp/daemon_output.txt"
405 EXEC_EXIT_CODE=0 424 EXEC_EXIT_CODE=0
406 eval "$CMD" > "$EXEC_OUTPUT" 2>&1 || EXEC_EXIT_CODE=$? 425 if [ "$USE_PULL_FALLBACK" = "true" ]; then
426 # Pull commands use registry-first, Docker Hub fallback
427 execute_pull_with_fallback "$CMD" > "$EXEC_OUTPUT" 2>&1 || EXEC_EXIT_CODE=$?
428 else
429 eval "$CMD" > "$EXEC_OUTPUT" 2>&1 || EXEC_EXIT_CODE=$?
430 fi
407 431
408 # Clean up shared directory 432 # Clean up shared directory
409 if [ "$NEEDS_INPUT" = "true" ]; then 433 if [ "$NEEDS_INPUT" = "true" ]; then
diff --git a/recipes-containers/vcontainer/files/vdkr-init.sh b/recipes-containers/vcontainer/files/vdkr-init.sh
index efe56049..318ce521 100755
--- a/recipes-containers/vcontainer/files/vdkr-init.sh
+++ b/recipes-containers/vcontainer/files/vdkr-init.sh
@@ -20,8 +20,10 @@
20# docker_output=<type> Output type: text, tar, storage (default: text) 20# docker_output=<type> Output type: text, tar, storage (default: text)
21# docker_state=<type> State type: none, disk (default: none) 21# docker_state=<type> State type: none, disk (default: none)
22# docker_network=1 Enable networking (configure eth0, DNS) 22# docker_network=1 Enable networking (configure eth0, DNS)
23# docker_registry=<url> Default registry for unqualified images (e.g., 10.0.2.2:5000/yocto)
24# docker_insecure_registry=<host:port> Mark registry as insecure (HTTP). Can repeat.
23# 25#
24# Version: 2.3.0 26# Version: 2.4.0
25 27
26# Set runtime-specific parameters before sourcing common code 28# Set runtime-specific parameters before sourcing common code
27VCONTAINER_RUNTIME_NAME="vdkr" 29VCONTAINER_RUNTIME_NAME="vdkr"
@@ -29,12 +31,31 @@ VCONTAINER_RUNTIME_CMD="docker"
29VCONTAINER_RUNTIME_PREFIX="docker" 31VCONTAINER_RUNTIME_PREFIX="docker"
30VCONTAINER_STATE_DIR="/var/lib/docker" 32VCONTAINER_STATE_DIR="/var/lib/docker"
31VCONTAINER_SHARE_NAME="vdkr_share" 33VCONTAINER_SHARE_NAME="vdkr_share"
32VCONTAINER_VERSION="2.3.0" 34VCONTAINER_VERSION="2.4.0"
35
36# Docker-specific: default registry for unqualified image names
37# Set via kernel param: docker_registry=10.0.2.2:5000/yocto
38# Or baked into rootfs: /etc/vdkr/registry.conf
39DOCKER_DEFAULT_REGISTRY=""
33 40
34# Source common init functions 41# Source common init functions
35# When installed as /init, common file is at /vcontainer-init-common.sh 42# When installed as /init, common file is at /vcontainer-init-common.sh
36. /vcontainer-init-common.sh 43. /vcontainer-init-common.sh
37 44
45# Load baked-in registry defaults from /etc/vdkr/registry.conf
46# These can be overridden by kernel cmdline parameters
47load_registry_config() {
48 if [ -f /etc/vdkr/registry.conf ]; then
49 . /etc/vdkr/registry.conf
50 # Map config file variables to our internal variables
51 if [ -n "$VDKR_DEFAULT_REGISTRY" ]; then
52 DOCKER_DEFAULT_REGISTRY="$VDKR_DEFAULT_REGISTRY"
53 log "Loaded baked registry: $DOCKER_DEFAULT_REGISTRY"
54 fi
55 # VDKR_INSECURE_REGISTRIES is handled in start_dockerd
56 fi
57}
58
38# ============================================================================ 59# ============================================================================
39# Docker-Specific Functions 60# Docker-Specific Functions
40# ============================================================================ 61# ============================================================================
@@ -100,6 +121,45 @@ start_dockerd() {
100 DOCKER_OPTS="$DOCKER_OPTS --exec-opt native.cgroupdriver=cgroupfs" 121 DOCKER_OPTS="$DOCKER_OPTS --exec-opt native.cgroupdriver=cgroupfs"
101 DOCKER_OPTS="$DOCKER_OPTS --log-level=info" 122 DOCKER_OPTS="$DOCKER_OPTS --log-level=info"
102 123
124 # Parse default registry from kernel cmdline (docker_registry=host:port/namespace)
125 # Kernel cmdline OVERRIDES baked config from /etc/vdkr/registry.conf
126 # This enables: "docker pull container-base" → "docker pull 10.0.2.2:5000/yocto/container-base"
127 GREP_RESULT=$(grep -o 'docker_registry=[^ ]*' /proc/cmdline 2>/dev/null || true)
128 if [ -n "$GREP_RESULT" ]; then
129 DOCKER_DEFAULT_REGISTRY=$(echo "$GREP_RESULT" | sed 's/docker_registry=//')
130 log "Registry from cmdline: $DOCKER_DEFAULT_REGISTRY"
131 elif [ -n "$DOCKER_DEFAULT_REGISTRY" ]; then
132 log "Registry from baked config: $DOCKER_DEFAULT_REGISTRY"
133 fi
134 if [ -n "$DOCKER_DEFAULT_REGISTRY" ]; then
135 # Extract host:port for insecure registry config (strip path/namespace)
136 REGISTRY_HOST=$(echo "$DOCKER_DEFAULT_REGISTRY" | cut -d'/' -f1)
137 # Auto-add to insecure registries if it looks like a local/private registry
138 if echo "$REGISTRY_HOST" | grep -qE '^(localhost|127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)'; then
139 DOCKER_OPTS="$DOCKER_OPTS --insecure-registry=$REGISTRY_HOST"
140 log "Auto-added insecure registry: $REGISTRY_HOST"
141 fi
142 fi
143
144 # Add baked insecure registries from /etc/vdkr/registry.conf
145 if [ -n "$VDKR_INSECURE_REGISTRIES" ]; then
146 for registry in $VDKR_INSECURE_REGISTRIES; do
147 DOCKER_OPTS="$DOCKER_OPTS --insecure-registry=$registry"
148 log "Added baked insecure registry: $registry"
149 done
150 fi
151
152 # Check for additional insecure registries from kernel cmdline (docker_insecure_registry=host:port)
153 # For local registry on build host via QEMU slirp: docker_insecure_registry=10.0.2.2:5000
154 # For remote HTTP registry: docker_insecure_registry=registry.company.com:5000
155 # Multiple registries can be specified by repeating the parameter
156 for registry in $(grep -o 'docker_insecure_registry=[^ ]*' /proc/cmdline 2>/dev/null | sed 's/docker_insecure_registry=//' || true); do
157 if [ -n "$registry" ]; then
158 DOCKER_OPTS="$DOCKER_OPTS --insecure-registry=$registry"
159 log "Added insecure registry: $registry"
160 fi
161 done
162
103 if [ "$CONTAINERD_READY" = "true" ]; then 163 if [ "$CONTAINERD_READY" = "true" ]; then
104 DOCKER_OPTS="$DOCKER_OPTS --containerd=/run/containerd/containerd.sock" 164 DOCKER_OPTS="$DOCKER_OPTS --containerd=/run/containerd/containerd.sock"
105 fi 165 fi
@@ -164,6 +224,219 @@ stop_runtime_daemons() {
164 fi 224 fi
165} 225}
166 226
227# Execute a pull command with registry fallback
228# Tries registry first, falls back to Docker Hub if image not found
229# Usage: execute_pull_with_fallback "docker pull alpine:latest"
230# Returns: exit code of successful pull, or last failure
231execute_pull_with_fallback() {
232 local cmd="$1"
233 local image=""
234 local tag=""
235
236 # Extract image name from pull command
237 # Handles: docker pull <image> or docker pull <image>:tag
238 if echo "$cmd" | grep -qE '^docker pull '; then
239 image=$(echo "$cmd" | awk '{print $3}')
240 else
241 # Not a pull command, just execute it
242 eval "$cmd"
243 return $?
244 fi
245
246 # If no registry configured, just run the original command
247 if [ -z "$DOCKER_DEFAULT_REGISTRY" ]; then
248 log "No registry configured, pulling from Docker Hub"
249 eval "$cmd"
250 return $?
251 fi
252
253 # Check if image is already qualified (has / in it)
254 if echo "$image" | grep -q '/'; then
255 # Already qualified (e.g., docker.io/library/alpine or myregistry/image)
256 log "Image already qualified: $image"
257 eval "$cmd"
258 return $?
259 fi
260
261 # Unqualified image - try registry first, then Docker Hub
262 local registry_image="$DOCKER_DEFAULT_REGISTRY/$image"
263
264 log "Trying registry first: $registry_image"
265 if docker pull "$registry_image" 2>/dev/null; then
266 log "Successfully pulled from registry: $registry_image"
267 docker images | grep -E "REPOSITORY|$image" || true
268 return 0
269 fi
270
271 log "Image not in registry, falling back to Docker Hub: $image"
272 if docker pull "$image"; then
273 log "Successfully pulled from Docker Hub: $image"
274 docker images | grep -E "REPOSITORY|$image" || true
275 return 0
276 fi
277
278 log "ERROR: Failed to pull $image from both registry and Docker Hub"
279 return 1
280}
281
282# Check if a command is a pull command that needs fallback handling
283is_pull_command() {
284 local cmd="$1"
285 echo "$cmd" | grep -qE '^docker pull '
286}
287
288# Helper function to transform an unqualified image name
289# Must be defined before transform_docker_command which uses it
290transform_image_name() {
291 local img="$1"
292 if [ -z "$img" ]; then
293 echo ""
294 return
295 fi
296 # Check if this is an image ID (hex string) - don't transform
297 # Short form: 12 hex chars (e7b39c54cdec)
298 # Long form: sha256:64 hex chars
299 if echo "$img" | grep -qE '^[0-9a-fA-F]{12,64}$'; then
300 echo "$img"
301 return
302 fi
303 if echo "$img" | grep -qE '^sha256:[0-9a-fA-F]{64}$'; then
304 echo "$img"
305 return
306 fi
307 # Check if image is unqualified (no /)
308 if ! echo "$img" | grep -q '/'; then
309 echo "$DOCKER_DEFAULT_REGISTRY/$img"
310 # Check if already has registry with port - don't transform
311 elif echo "$img" | grep -qE '^[^/]+:[0-9]+/'; then
312 echo "$img"
313 # Check if looks like a domain - don't transform
314 elif echo "$img" | grep -qE '^[a-zA-Z0-9-]+\.[a-zA-Z]'; then
315 echo "$img"
316 else
317 echo "$img"
318 fi
319}
320
321# Transform docker commands to use default registry for unqualified images
322# "docker pull container-base" → "docker pull 10.0.2.2:5000/yocto/container-base"
323# "docker pull alpine" → "docker pull 10.0.2.2:5000/yocto/alpine" (if registry set)
324# "docker pull docker.io/library/alpine" → unchanged (already qualified)
325# Also handles "docker image *" compound commands and other image commands
326#
327# NOTE: Pull commands are NOT transformed here - they use execute_pull_with_fallback
328# which tries registry first, then Docker Hub as fallback.
329transform_docker_command() {
330 local cmd="$1"
331
332 # Handle "docker image *" compound commands - convert to standard form
333 # docker image pull → docker pull
334 # docker image rm → docker rmi
335 # docker image ls → docker images
336 # docker image inspect → docker inspect (works for images)
337 if echo "$cmd" | grep -qE '^docker image '; then
338 local subcmd=$(echo "$cmd" | awk '{print $3}')
339 local rest=$(echo "$cmd" | cut -d' ' -f4-)
340 case "$subcmd" in
341 pull) cmd="docker pull $rest" ;;
342 rm) cmd="docker rmi $rest" ;;
343 ls) cmd="docker images $rest" ;;
344 inspect) cmd="docker inspect $rest" ;;
345 tag) cmd="docker tag $rest" ;;
346 push) cmd="docker push $rest" ;;
347 prune) cmd="docker image prune $rest" ;; # keep as-is, docker supports it
348 history) cmd="docker history $rest" ;;
349 *) ;; # pass through unknown subcommands
350 esac
351 fi
352
353 # Only transform if default registry is configured
354 if [ -z "$DOCKER_DEFAULT_REGISTRY" ]; then
355 echo "$cmd"
356 return
357 fi
358
359 # NOTE: docker images, inspect, history, rmi, tag do NOT get transformed.
360 # These commands operate on local images - the user specifies exactly what they have.
361 # Transform only applies to pull/run where we're fetching images.
362 #
363 # If user has:
364 # - alpine:latest (from Docker Hub via fallback)
365 # - 10.0.2.2:5000/yocto/myapp:latest (from registry)
366 #
367 # Then:
368 # - "docker images alpine" → shows alpine:latest (no transform)
369 # - "docker inspect alpine" → inspects alpine:latest (no transform)
370 # - "docker rmi alpine" → removes alpine:latest (no transform)
371
372 # Pull commands are handled by execute_pull_with_fallback, not transformed here
373 if echo "$cmd" | grep -qE '^docker pull '; then
374 echo "$cmd"
375 return
376 fi
377
378 # Check if this is a run command
379 if echo "$cmd" | grep -qE '^docker run '; then
380 # Extract the image reference (handles "docker run [opts] img [cmd]")
381 local docker_cmd="run"
382 local rest=""
383
384 if [ "$docker_cmd" = "run" ]; then
385 # docker run [options] <image> [command]
386 # This is trickier - image is the first non-option argument
387 # For simplicity, look for image pattern after run
388 # Skip known options that take arguments
389 local args=$(echo "$cmd" | cut -d' ' -f3-)
390 local image=""
391 local new_args=""
392 local skip_next=false
393
394 for arg in $args; do
395 if [ "$skip_next" = "true" ]; then
396 new_args="$new_args $arg"
397 skip_next=false
398 continue
399 fi
400
401 case "$arg" in
402 -d|--detach|-i|--interactive|-t|--tty|--rm|--privileged)
403 new_args="$new_args $arg"
404 ;;
405 -p|--publish|-v|--volume|-e|--env|--name|--network|-w|--workdir|--entrypoint)
406 new_args="$new_args $arg"
407 skip_next=true
408 ;;
409 -p=*|--publish=*|-v=*|--volume=*|-e=*|--env=*|--name=*|--network=*|-w=*|--workdir=*|--entrypoint=*)
410 new_args="$new_args $arg"
411 ;;
412 -*)
413 # Other options, pass through
414 new_args="$new_args $arg"
415 ;;
416 *)
417 # First non-option is the image
418 if [ -z "$image" ]; then
419 image="$arg"
420 else
421 # Rest is the command
422 rest="$rest $arg"
423 fi
424 ;;
425 esac
426 done
427
428 if [ -n "$image" ]; then
429 local transformed=$(transform_image_name "$image")
430 echo "docker run$new_args $transformed$rest"
431 return
432 fi
433 fi
434 fi
435
436 # Return unchanged
437 echo "$cmd"
438}
439
167handle_storage_output() { 440handle_storage_output() {
168 echo "Stopping Docker gracefully..." 441 echo "Stopping Docker gracefully..."
169 /usr/bin/docker system prune -f >/dev/null 2>&1 || true 442 /usr/bin/docker system prune -f >/dev/null 2>&1 || true
@@ -223,15 +496,36 @@ mount_input_disk
223# Configure networking 496# Configure networking
224configure_networking 497configure_networking
225 498
499# Load baked registry config (can be overridden by kernel cmdline)
500load_registry_config
501
226# Start containerd and dockerd (Docker-specific) 502# Start containerd and dockerd (Docker-specific)
227start_containerd 503start_containerd
228start_dockerd 504start_dockerd
229 505
230# Handle daemon mode or single command execution 506# Handle daemon mode or single command execution
231if [ "$RUNTIME_DAEMON" = "1" ]; then 507if [ "$RUNTIME_DAEMON" = "1" ]; then
508 # Export registry for daemon mode
509 # Note: Functions (execute_pull_with_fallback, is_pull_command) are already
510 # available since they're defined in this script before run_daemon_mode is called
511 export DOCKER_DEFAULT_REGISTRY
232 run_daemon_mode 512 run_daemon_mode
233else 513else
234 prepare_input_path 514 prepare_input_path
515 # Check if this is a pull command - use fallback logic
516 if is_pull_command "$RUNTIME_CMD"; then
517 # Pull commands use registry-first, Docker Hub fallback
518 log "Using pull with registry fallback"
519 execute_pull_with_fallback "$RUNTIME_CMD"
520 EXEC_EXIT_CODE=$?
521 echo "===EXIT_CODE=$EXEC_EXIT_CODE==="
522 graceful_shutdown
523 exit 0
524 fi
525 # Transform other commands to use default registry for unqualified images
526 if [ -n "$DOCKER_DEFAULT_REGISTRY" ]; then
527 RUNTIME_CMD=$(transform_docker_command "$RUNTIME_CMD")
528 fi
235 execute_command 529 execute_command
236fi 530fi
237 531
diff --git a/recipes-containers/vcontainer/files/vrunner.sh b/recipes-containers/vcontainer/files/vrunner.sh
index cf6aafd4..af9b855c 100755
--- a/recipes-containers/vcontainer/files/vrunner.sh
+++ b/recipes-containers/vcontainer/files/vrunner.sh
@@ -120,6 +120,8 @@ OPTIONS:
120 --output <path> Output file for tar/storage output types 120 --output <path> Output file for tar/storage output types
121 --blob-dir <path> Directory containing kernel/initramfs blobs 121 --blob-dir <path> Directory containing kernel/initramfs blobs
122 --network, -n Enable networking (slirp user-mode, outbound only) 122 --network, -n Enable networking (slirp user-mode, outbound only)
123 --registry <url> Default registry for unqualified images (e.g., 10.0.2.2:5000/yocto)
124 --insecure-registry <host:port> Mark registry as insecure (HTTP). Can repeat.
123 --interactive, -it Run in interactive mode (connects terminal to container) 125 --interactive, -it Run in interactive mode (connects terminal to container)
124 --timeout <secs> QEMU timeout [default: 300] 126 --timeout <secs> QEMU timeout [default: 300]
125 --idle-timeout <s> Daemon idle timeout in seconds [default: 1800] 127 --idle-timeout <s> Daemon idle timeout in seconds [default: 1800]
@@ -166,6 +168,11 @@ EXAMPLES:
166 # Pull an image from a registry (requires --network) 168 # Pull an image from a registry (requires --network)
167 vrunner.sh --network -- docker pull alpine:latest 169 vrunner.sh --network -- docker pull alpine:latest
168 170
171 # Pull from local registry using default registry prefix
172 vrunner.sh --network --registry 10.0.2.2:5000/yocto \
173 -- docker pull container-base
174 # This becomes: docker pull 10.0.2.2:5000/yocto/container-base
175
169 # Batch import multiple OCI containers in one session 176 # Batch import multiple OCI containers in one session
170 vrunner.sh --batch-import --output storage.tar \ 177 vrunner.sh --batch-import --output storage.tar \
171 -- /path/to/app-oci:myapp:latest /path/to/db-oci:mydb:v1.0 178 -- /path/to/app-oci:myapp:latest /path/to/db-oci:mydb:v1.0
@@ -191,6 +198,10 @@ DISABLE_KVM="false"
191DOCKER_CMD="" 198DOCKER_CMD=""
192PORT_FORWARDS=() 199PORT_FORWARDS=()
193 200
201# Registry configuration
202DOCKER_REGISTRY=""
203INSECURE_REGISTRIES=()
204
194# Batch import mode 205# Batch import mode
195BATCH_IMPORT="false" 206BATCH_IMPORT="false"
196 207
@@ -250,6 +261,16 @@ while [ $# -gt 0 ]; do
250 PORT_FORWARDS+=("$2") 261 PORT_FORWARDS+=("$2")
251 shift 2 262 shift 2
252 ;; 263 ;;
264 --registry)
265 # Default registry for unqualified images (e.g., 10.0.2.2:5000/yocto)
266 DOCKER_REGISTRY="$2"
267 shift 2
268 ;;
269 --insecure-registry)
270 # Mark a registry as insecure (HTTP)
271 INSECURE_REGISTRIES+=("$2")
272 shift 2
273 ;;
253 --interactive|-it) 274 --interactive|-it)
254 INTERACTIVE="true" 275 INTERACTIVE="true"
255 shift 276 shift
@@ -984,6 +1005,16 @@ if [ "$NETWORK" = "true" ]; then
984 KERNEL_APPEND="$KERNEL_APPEND ${CMDLINE_PREFIX}_network=1" 1005 KERNEL_APPEND="$KERNEL_APPEND ${CMDLINE_PREFIX}_network=1"
985fi 1006fi
986 1007
1008# Registry configuration for unqualified image names
1009if [ -n "$DOCKER_REGISTRY" ]; then
1010 KERNEL_APPEND="$KERNEL_APPEND ${CMDLINE_PREFIX}_registry=$DOCKER_REGISTRY"
1011fi
1012
1013# Insecure registries (HTTP)
1014for reg in "${INSECURE_REGISTRIES[@]}"; do
1015 KERNEL_APPEND="$KERNEL_APPEND ${CMDLINE_PREFIX}_insecure_registry=$reg"
1016done
1017
987# Tell init script if interactive mode 1018# Tell init script if interactive mode
988if [ "$INTERACTIVE" = "true" ]; then 1019if [ "$INTERACTIVE" = "true" ]; then
989 KERNEL_APPEND="$KERNEL_APPEND ${CMDLINE_PREFIX}_interactive=1" 1020 KERNEL_APPEND="$KERNEL_APPEND ${CMDLINE_PREFIX}_interactive=1"
diff --git a/recipes-containers/vcontainer/vdkr-rootfs-image.bb b/recipes-containers/vcontainer/vdkr-rootfs-image.bb
index 079f4c17..4a0c8a10 100644
--- a/recipes-containers/vcontainer/vdkr-rootfs-image.bb
+++ b/recipes-containers/vcontainer/vdkr-rootfs-image.bb
@@ -11,6 +11,12 @@
11# Build with: 11# Build with:
12# bitbake mc:vruntime-aarch64:vdkr-rootfs-image 12# bitbake mc:vruntime-aarch64:vdkr-rootfs-image
13# bitbake mc:vruntime-x86-64:vdkr-rootfs-image 13# bitbake mc:vruntime-x86-64:vdkr-rootfs-image
14#
15# Optional baked-in registry defaults (can still be overridden via CLI):
16# Uses the same variables as container-registry infrastructure:
17# CONTAINER_REGISTRY_URL = "10.0.2.2:5000"
18# CONTAINER_REGISTRY_NAMESPACE = "yocto"
19# CONTAINER_REGISTRY_INSECURE = "1" (or DOCKER_REGISTRY_INSECURE)
14 20
15SUMMARY = "Minimal Docker rootfs for vdkr" 21SUMMARY = "Minimal Docker rootfs for vdkr"
16DESCRIPTION = "A minimal image containing Docker tools for use with vdkr. \ 22DESCRIPTION = "A minimal image containing Docker tools for use with vdkr. \
@@ -51,6 +57,13 @@ IMAGE_FEATURES = ""
51IMAGE_ROOTFS_SIZE = "524288" 57IMAGE_ROOTFS_SIZE = "524288"
52IMAGE_ROOTFS_EXTRA_SPACE = "0" 58IMAGE_ROOTFS_EXTRA_SPACE = "0"
53 59
60# Registry defaults - reuse common container-registry variables
61# Empty URL means no baked config (can still configure via CLI)
62CONTAINER_REGISTRY_URL ?= ""
63CONTAINER_REGISTRY_NAMESPACE ?= "yocto"
64CONTAINER_REGISTRY_INSECURE ?= "0"
65DOCKER_REGISTRY_INSECURE ?= ""
66
54# Use squashfs for smaller size (~3x compression) 67# Use squashfs for smaller size (~3x compression)
55# The preinit mounts squashfs read-only with tmpfs overlay for writes 68# The preinit mounts squashfs read-only with tmpfs overlay for writes
56IMAGE_FSTYPES = "squashfs" 69IMAGE_FSTYPES = "squashfs"
@@ -72,4 +85,39 @@ install_vdkr_init() {
72 # Create skopeo policy 85 # Create skopeo policy
73 install -d ${IMAGE_ROOTFS}/etc/containers 86 install -d ${IMAGE_ROOTFS}/etc/containers
74 echo '{"default":[{"type":"insecureAcceptAnything"}]}' > ${IMAGE_ROOTFS}/etc/containers/policy.json 87 echo '{"default":[{"type":"insecureAcceptAnything"}]}' > ${IMAGE_ROOTFS}/etc/containers/policy.json
88
89 # Create baked-in registry config if specified
90 # Uses common CONTAINER_REGISTRY_* variables for consistency
91 # These defaults can be overridden via kernel cmdline (docker_registry=)
92 #
93 # NOTE: localhost URLs are auto-translated to 10.0.2.2 for QEMU slirp networking
94 # This allows CONTAINER_REGISTRY_URL=localhost:5000 to work for both:
95 # - Host-side operations (registry script, pushing)
96 # - vdkr inside QEMU (via 10.0.2.2 slirp gateway)
97 install -d ${IMAGE_ROOTFS}/etc/vdkr
98 if [ -n "${CONTAINER_REGISTRY_URL}" ]; then
99 cat > ${IMAGE_ROOTFS}/etc/vdkr/registry.conf << 'VDKR_EOF'
100# vdkr registry defaults (baked at build time)
101# These can be overridden via:
102# - Kernel cmdline: docker_registry=... docker_insecure_registry=...
103# - vdkr CLI: vdkr --registry ... or vdkr vconfig registry ...
104VDKR_EOF
105 # Build registry URL with namespace
106 # Translate localhost to 10.0.2.2 for QEMU slirp networking
107 QEMU_REGISTRY_URL=$(echo "${CONTAINER_REGISTRY_URL}" | sed 's/^localhost/10.0.2.2/' | sed 's/^127\.0\.0\.1/10.0.2.2/')
108 echo "VDKR_DEFAULT_REGISTRY=\"${QEMU_REGISTRY_URL}/${CONTAINER_REGISTRY_NAMESPACE}\"" >> ${IMAGE_ROOTFS}/etc/vdkr/registry.conf
109
110 # Handle insecure registries - check both DOCKER_REGISTRY_INSECURE and CONTAINER_REGISTRY_INSECURE
111 INSECURE_LIST="${DOCKER_REGISTRY_INSECURE}"
112 if [ "${CONTAINER_REGISTRY_INSECURE}" = "1" ] && [ -n "${QEMU_REGISTRY_URL}" ]; then
113 # Use the QEMU-translated URL for insecure list
114 INSECURE_LIST="${INSECURE_LIST} ${QEMU_REGISTRY_URL}"
115 fi
116 # Also translate any localhost entries in the insecure list
117 INSECURE_LIST=$(echo "${INSECURE_LIST}" | sed 's/localhost/10.0.2.2/g' | sed 's/127\.0\.0\.1/10.0.2.2/g')
118 if [ -n "${INSECURE_LIST}" ]; then
119 echo "VDKR_INSECURE_REGISTRIES=\"${INSECURE_LIST}\"" >> ${IMAGE_ROOTFS}/etc/vdkr/registry.conf
120 fi
121 bbnote "Created vdkr registry config: ${QEMU_REGISTRY_URL}/${CONTAINER_REGISTRY_NAMESPACE}"
122 fi
75} 123}