diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-19 01:53:36 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-26 01:05:01 +0000 |
| commit | 035e0daebeb53880ea2a6bd0f0e31785f3ec9e55 (patch) | |
| tree | 1ae711e61d79ad2f7b0afba6fc4489f61d1a6202 /recipes-containers/container-registry | |
| parent | bf5abfe3d55604c6b22416cc23cbfaba1ff7bee2 (diff) | |
| download | meta-virtualization-035e0daebeb53880ea2a6bd0f0e31785f3ec9e55.tar.gz | |
vxn: add Docker/Podman integration and CLI frontends
Add vdkr/vpdmn as Dom0 target packages with Xen auto-detection,
native Docker/Podman config sub-packages, and OCI runtime fixes
for Docker compatibility (JSON logging, root.path, kill --all,
monitor PID lifecycle).
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/container-registry')
| -rw-r--r-- | recipes-containers/container-registry/container-oci-registry-config.bb | 145 | ||||
| -rw-r--r-- | recipes-containers/container-registry/docker-registry-config.bb | 24 |
2 files changed, 108 insertions, 61 deletions
diff --git a/recipes-containers/container-registry/container-oci-registry-config.bb b/recipes-containers/container-registry/container-oci-registry-config.bb index 294defc3..5161f0a3 100644 --- a/recipes-containers/container-registry/container-oci-registry-config.bb +++ b/recipes-containers/container-registry/container-oci-registry-config.bb | |||
| @@ -64,12 +64,19 @@ CONTAINER_REGISTRY_SEARCH_FIRST ?= "1" | |||
| 64 | # NOT stored in bitbake - should point to external file | 64 | # NOT stored in bitbake - should point to external file |
| 65 | CONTAINER_REGISTRY_AUTHFILE ?= "" | 65 | CONTAINER_REGISTRY_AUTHFILE ?= "" |
| 66 | 66 | ||
| 67 | # OCI runtime configuration for Podman | ||
| 68 | # Runtime name (e.g. "vxn") — creates a containers.conf.d drop-in | ||
| 69 | PODMAN_OCI_RUNTIME ?= "" | ||
| 70 | # Path to OCI runtime binary (e.g. "/usr/bin/vxn-oci-runtime") | ||
| 71 | PODMAN_OCI_RUNTIME_PATH ?= "" | ||
| 72 | |||
| 67 | # Skip recipe entirely if not configured | 73 | # Skip recipe entirely if not configured |
| 68 | # User must explicitly set CONTAINER_REGISTRY_URL to enable | 74 | # User must explicitly set CONTAINER_REGISTRY_URL to enable |
| 69 | python() { | 75 | python() { |
| 70 | registry = d.getVar('CONTAINER_REGISTRY_URL') | 76 | registry = d.getVar('CONTAINER_REGISTRY_URL') |
| 71 | if not registry: | 77 | oci_runtime = (d.getVar('PODMAN_OCI_RUNTIME') or "").strip() |
| 72 | raise bb.parse.SkipRecipe("CONTAINER_REGISTRY_URL not set - recipe is opt-in only") | 78 | if not registry and not oci_runtime: |
| 79 | raise bb.parse.SkipRecipe("No registry or OCI runtime configured - recipe is opt-in only") | ||
| 73 | 80 | ||
| 74 | # Check for conflicting settings | 81 | # Check for conflicting settings |
| 75 | secure = d.getVar('CONTAINER_REGISTRY_SECURE') == '1' | 82 | secure = d.getVar('CONTAINER_REGISTRY_SECURE') == '1' |
| @@ -94,63 +101,85 @@ python do_install() { | |||
| 94 | search_first = d.getVar('CONTAINER_REGISTRY_SEARCH_FIRST') == "1" | 101 | search_first = d.getVar('CONTAINER_REGISTRY_SEARCH_FIRST') == "1" |
| 95 | ca_cert = d.getVar('CONTAINER_REGISTRY_CA_CERT') | 102 | ca_cert = d.getVar('CONTAINER_REGISTRY_CA_CERT') |
| 96 | authfile = d.getVar('CONTAINER_REGISTRY_AUTHFILE') or '' | 103 | authfile = d.getVar('CONTAINER_REGISTRY_AUTHFILE') or '' |
| 97 | 104 | oci_runtime = (d.getVar('PODMAN_OCI_RUNTIME') or "").strip() | |
| 98 | # Extract registry host (strip any path) | 105 | oci_runtime_path = (d.getVar('PODMAN_OCI_RUNTIME_PATH') or "").strip() |
| 99 | registry_host = registry.split('/')[0] if '/' in registry else registry | ||
| 100 | 106 | ||
| 101 | dest = d.getVar('D') | 107 | dest = d.getVar('D') |
| 102 | confdir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'), | ||
| 103 | 'containers', 'registries.conf.d') | ||
| 104 | os.makedirs(confdir, exist_ok=True) | ||
| 105 | 108 | ||
| 106 | # Install CA cert in secure mode | 109 | # --- Registry configuration --- |
| 107 | if secure: | 110 | if registry: |
| 108 | if os.path.exists(ca_cert): | 111 | # Extract registry host (strip any path) |
| 109 | cert_dir = os.path.join(dest, 'etc/containers/certs.d', registry_host) | 112 | registry_host = registry.split('/')[0] if '/' in registry else registry |
| 110 | os.makedirs(cert_dir, exist_ok=True) | 113 | |
| 111 | shutil.copy(ca_cert, os.path.join(cert_dir, 'ca.crt')) | 114 | confdir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'), |
| 112 | bb.note(f"Installed CA certificate for registry: {registry_host}") | 115 | 'containers', 'registries.conf.d') |
| 113 | else: | 116 | os.makedirs(confdir, exist_ok=True) |
| 114 | bb.warn(f"Secure mode enabled but CA certificate not found at {ca_cert}") | 117 | |
| 115 | bb.warn("Run 'container-registry.sh start' to generate PKI, then rebuild this package") | 118 | # Install CA cert in secure mode |
| 116 | |||
| 117 | # In secure mode, insecure should be false | ||
| 118 | if secure: | ||
| 119 | insecure = False | ||
| 120 | |||
| 121 | # Generate drop-in config | ||
| 122 | # Filename starts with 50- so it's processed after base config but | ||
| 123 | # can be overridden by higher-numbered files | ||
| 124 | config_path = os.path.join(confdir, '50-custom-registry.conf') | ||
| 125 | |||
| 126 | with open(config_path, 'w') as f: | ||
| 127 | f.write(f"# Custom container registry: {registry}\n") | ||
| 128 | f.write(f"# Generated by container-oci-registry-config recipe\n") | ||
| 129 | f.write(f"# This is ADDITIVE - base registries.conf is unchanged\n") | ||
| 130 | f.write(f"# Public registries (docker.io, quay.io) remain accessible\n") | ||
| 131 | f.write(f"#\n") | ||
| 132 | if secure: | 119 | if secure: |
| 133 | f.write(f"# Mode: secure (TLS with CA certificate verification)\n") | 120 | if os.path.exists(ca_cert): |
| 134 | f.write(f"# CA cert: /etc/containers/certs.d/{registry_host}/ca.crt\n") | 121 | cert_dir = os.path.join(dest, 'etc/containers/certs.d', registry_host) |
| 135 | else: | 122 | os.makedirs(cert_dir, exist_ok=True) |
| 136 | f.write(f"# Mode: insecure (HTTP or untrusted TLS)\n") | 123 | shutil.copy(ca_cert, os.path.join(cert_dir, 'ca.crt')) |
| 137 | f.write(f"#\n") | 124 | bb.note("Installed CA certificate for registry: %s" % registry_host) |
| 138 | f.write(f"# To remove: uninstall container-oci-registry-config package\n") | 125 | else: |
| 139 | f.write(f"# or delete this file\n\n") | 126 | bb.warn("Secure mode enabled but CA certificate not found at %s" % ca_cert) |
| 140 | 127 | bb.warn("Run 'container-registry.sh start' to generate PKI, then rebuild this package") | |
| 141 | if search_first: | 128 | |
| 142 | # Add to unqualified-search-registries | 129 | # In secure mode, insecure should be false |
| 143 | # This means short names like "myapp:latest" will search here first | 130 | if secure: |
| 144 | f.write(f"# Search this registry for unqualified image names\n") | 131 | insecure = False |
| 145 | f.write(f'unqualified-search-registries = ["{registry}"]\n\n') | 132 | |
| 146 | 133 | # Generate drop-in config | |
| 147 | # Always create registry entry to set insecure flag explicitly | 134 | config_path = os.path.join(confdir, '50-custom-registry.conf') |
| 148 | f.write(f'[[registry]]\n') | 135 | |
| 149 | f.write(f'location = "{registry_host}"\n') | 136 | with open(config_path, 'w') as f: |
| 150 | if insecure: | 137 | f.write("# Custom container registry: %s\n" % registry) |
| 151 | f.write(f'insecure = true\n') | 138 | f.write("# Generated by container-oci-registry-config recipe\n") |
| 152 | else: | 139 | f.write("# This is ADDITIVE - base registries.conf is unchanged\n") |
| 153 | f.write(f'insecure = false\n') | 140 | f.write("# Public registries (docker.io, quay.io) remain accessible\n") |
| 141 | f.write("#\n") | ||
| 142 | if secure: | ||
| 143 | f.write("# Mode: secure (TLS with CA certificate verification)\n") | ||
| 144 | f.write("# CA cert: /etc/containers/certs.d/%s/ca.crt\n" % registry_host) | ||
| 145 | else: | ||
| 146 | f.write("# Mode: insecure (HTTP or untrusted TLS)\n") | ||
| 147 | f.write("#\n") | ||
| 148 | f.write("# To remove: uninstall container-oci-registry-config package\n") | ||
| 149 | f.write("# or delete this file\n\n") | ||
| 150 | |||
| 151 | if search_first: | ||
| 152 | f.write("# Search this registry for unqualified image names\n") | ||
| 153 | f.write('unqualified-search-registries = ["%s"]\n\n' % registry) | ||
| 154 | |||
| 155 | f.write('[[registry]]\n') | ||
| 156 | f.write('location = "%s"\n' % registry_host) | ||
| 157 | if insecure: | ||
| 158 | f.write('insecure = true\n') | ||
| 159 | else: | ||
| 160 | f.write('insecure = false\n') | ||
| 161 | |||
| 162 | mode = "secure" if secure else ("insecure" if insecure else "default") | ||
| 163 | bb.note("Created registry config for %s (mode=%s)" % (registry, mode)) | ||
| 164 | |||
| 165 | # --- OCI runtime configuration --- | ||
| 166 | if oci_runtime: | ||
| 167 | dropin_dir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'), | ||
| 168 | 'containers', 'containers.conf.d') | ||
| 169 | os.makedirs(dropin_dir, exist_ok=True) | ||
| 170 | |||
| 171 | runtime_path = oci_runtime_path if oci_runtime_path else "/usr/bin/%s" % oci_runtime | ||
| 172 | dropin_path = os.path.join(dropin_dir, '50-oci-runtime.conf') | ||
| 173 | |||
| 174 | with open(dropin_path, 'w') as f: | ||
| 175 | f.write("# OCI runtime configuration\n") | ||
| 176 | f.write("# Generated by container-oci-registry-config recipe\n\n") | ||
| 177 | f.write("[engine]\n") | ||
| 178 | f.write('runtime = "%s"\n\n' % oci_runtime) | ||
| 179 | f.write("[engine.runtimes]\n") | ||
| 180 | f.write('%s = ["%s"]\n' % (oci_runtime, runtime_path)) | ||
| 181 | |||
| 182 | bb.note("Created OCI runtime drop-in for %s (%s)" % (oci_runtime, runtime_path)) | ||
| 154 | 183 | ||
| 155 | # Install authfile if provided (for baked credentials) | 184 | # Install authfile if provided (for baked credentials) |
| 156 | if authfile and os.path.exists(authfile): | 185 | if authfile and os.path.exists(authfile): |
| @@ -159,14 +188,12 @@ python do_install() { | |||
| 159 | auth_json = os.path.join(containers_dir, 'auth.json') | 188 | auth_json = os.path.join(containers_dir, 'auth.json') |
| 160 | shutil.copy(authfile, auth_json) | 189 | shutil.copy(authfile, auth_json) |
| 161 | os.chmod(auth_json, 0o600) | 190 | os.chmod(auth_json, 0o600) |
| 162 | bb.note(f"Installed OCI auth config from {authfile}") | 191 | bb.note("Installed OCI auth config from %s" % authfile) |
| 163 | |||
| 164 | mode = "secure" if secure else ("insecure" if insecure else "default") | ||
| 165 | bb.note(f"Created registry config for {registry} (mode={mode})") | ||
| 166 | } | 192 | } |
| 167 | 193 | ||
| 168 | FILES:${PN} = " \ | 194 | FILES:${PN} = " \ |
| 169 | ${sysconfdir}/containers/registries.conf.d \ | 195 | ${sysconfdir}/containers/registries.conf.d \ |
| 196 | ${sysconfdir}/containers/containers.conf.d \ | ||
| 170 | ${sysconfdir}/containers/certs.d/*/ca.crt \ | 197 | ${sysconfdir}/containers/certs.d/*/ca.crt \ |
| 171 | ${sysconfdir}/containers/auth.json \ | 198 | ${sysconfdir}/containers/auth.json \ |
| 172 | " | 199 | " |
diff --git a/recipes-containers/container-registry/docker-registry-config.bb b/recipes-containers/container-registry/docker-registry-config.bb index 0e8d66ad..e558cccb 100644 --- a/recipes-containers/container-registry/docker-registry-config.bb +++ b/recipes-containers/container-registry/docker-registry-config.bb | |||
| @@ -60,6 +60,13 @@ DOCKER_REGISTRY_INSECURE ?= "" | |||
| 60 | # NOT stored in bitbake - should point to external file | 60 | # NOT stored in bitbake - should point to external file |
| 61 | CONTAINER_REGISTRY_AUTHFILE ?= "" | 61 | CONTAINER_REGISTRY_AUTHFILE ?= "" |
| 62 | 62 | ||
| 63 | # OCI runtime configuration for Docker daemon | ||
| 64 | # JSON object mapping runtime names to paths, e.g.: | ||
| 65 | # DOCKER_OCI_RUNTIMES = '{"vxn": {"path": "/usr/bin/vxn-oci-runtime"}}' | ||
| 66 | DOCKER_OCI_RUNTIMES ?= "" | ||
| 67 | # Default OCI runtime name (must be a key in DOCKER_OCI_RUNTIMES or "runc") | ||
| 68 | DOCKER_DEFAULT_RUNTIME ?= "" | ||
| 69 | |||
| 63 | def get_insecure_registries(d): | 70 | def get_insecure_registries(d): |
| 64 | """Get insecure registries from either Docker-specific or generic config""" | 71 | """Get insecure registries from either Docker-specific or generic config""" |
| 65 | # Prefer explicit DOCKER_REGISTRY_INSECURE if set | 72 | # Prefer explicit DOCKER_REGISTRY_INSECURE if set |
| @@ -87,8 +94,10 @@ python() { | |||
| 87 | bb.fatal("CONTAINER_REGISTRY_SECURE='1' conflicts with insecure registry settings. " | 94 | bb.fatal("CONTAINER_REGISTRY_SECURE='1' conflicts with insecure registry settings. " |
| 88 | "Use secure mode (TLS+auth) OR insecure mode (HTTP), not both.") | 95 | "Use secure mode (TLS+auth) OR insecure mode (HTTP), not both.") |
| 89 | 96 | ||
| 90 | if not secure and not registries: | 97 | oci_runtimes = (d.getVar('DOCKER_OCI_RUNTIMES') or "").strip() |
| 91 | raise bb.parse.SkipRecipe("No registry configured - recipe is opt-in only") | 98 | |
| 99 | if not secure and not registries and not oci_runtimes: | ||
| 100 | raise bb.parse.SkipRecipe("No registry or OCI runtime configured - recipe is opt-in only") | ||
| 92 | 101 | ||
| 93 | # In secure mode, depend on PKI generation | 102 | # In secure mode, depend on PKI generation |
| 94 | if secure: | 103 | if secure: |
| @@ -137,6 +146,17 @@ python do_install() { | |||
| 137 | config["insecure-registries"] = registries | 146 | config["insecure-registries"] = registries |
| 138 | bb.note(f"Created Docker config with insecure registries: {registries}") | 147 | bb.note(f"Created Docker config with insecure registries: {registries}") |
| 139 | 148 | ||
| 149 | # OCI runtime configuration | ||
| 150 | oci_runtimes = (d.getVar('DOCKER_OCI_RUNTIMES') or "").strip() | ||
| 151 | default_runtime = (d.getVar('DOCKER_DEFAULT_RUNTIME') or "").strip() | ||
| 152 | |||
| 153 | if oci_runtimes: | ||
| 154 | config["runtimes"] = json.loads(oci_runtimes) | ||
| 155 | bb.note("Added OCI runtimes to Docker config: %s" % oci_runtimes) | ||
| 156 | if default_runtime: | ||
| 157 | config["default-runtime"] = default_runtime | ||
| 158 | bb.note("Set default Docker runtime: %s" % default_runtime) | ||
| 159 | |||
| 140 | # Install authfile if provided (for baked credentials) | 160 | # Install authfile if provided (for baked credentials) |
| 141 | if authfile and os.path.exists(authfile): | 161 | if authfile and os.path.exists(authfile): |
| 142 | docker_dir = os.path.join(dest, 'root/.docker') | 162 | docker_dir = os.path.join(dest, 'root/.docker') |
