From 3997635db41e146246c693ce88243d8d744fe76d Mon Sep 17 00:00:00 2001 From: Shion Tanaka Date: Sat, 3 Jan 2026 11:54:05 +0900 Subject: [PATCH] feat: Add Fedora, UBI, and Podman-in-Podman templates - Add Fedora template with multiple version options (41, 42, 43, latest, rawhide) - Add UBI template with version 8/9/10 and variants (ubi, ubi-minimal, ubi-init) - Add Podman-in-Podman template for nested container operations Signed-off-by: Shion Tanaka --- .github/workflows/test-pr.yaml | 3 + src/fedora/.devcontainer/Dockerfile | 27 ++++ src/fedora/.devcontainer/devcontainer.json | 40 +++++ src/fedora/NOTES.md | 44 +++++ src/fedora/devcontainer-template.json | 28 ++++ src/podman-in-podman/.devcontainer/Dockerfile | 97 +++++++++++ .../.devcontainer/devcontainer.json | 65 ++++++++ src/podman-in-podman/NOTES.md | 93 +++++++++++ .../devcontainer-template.json | 33 ++++ src/ubi/.devcontainer/Dockerfile | 51 ++++++ src/ubi/.devcontainer/devcontainer.json | 41 +++++ src/ubi/NOTES.md | 55 +++++++ src/ubi/devcontainer-template.json | 36 +++++ test/fedora/test-utils-fedora.sh | 150 ++++++++++++++++++ test/fedora/test.sh | 13 ++ test/podman-in-podman/test-utils-fedora.sh | 150 ++++++++++++++++++ test/podman-in-podman/test.sh | 42 +++++ test/ubi/test-utils-ubi.sh | 150 ++++++++++++++++++ test/ubi/test.sh | 14 ++ 19 files changed, 1132 insertions(+) create mode 100644 src/fedora/.devcontainer/Dockerfile create mode 100644 src/fedora/.devcontainer/devcontainer.json create mode 100644 src/fedora/NOTES.md create mode 100644 src/fedora/devcontainer-template.json create mode 100644 src/podman-in-podman/.devcontainer/Dockerfile create mode 100644 src/podman-in-podman/.devcontainer/devcontainer.json create mode 100644 src/podman-in-podman/NOTES.md create mode 100644 src/podman-in-podman/devcontainer-template.json create mode 100644 src/ubi/.devcontainer/Dockerfile create mode 100644 src/ubi/.devcontainer/devcontainer.json create mode 100644 src/ubi/NOTES.md create mode 100644 src/ubi/devcontainer-template.json create mode 100755 test/fedora/test-utils-fedora.sh create mode 100755 test/fedora/test.sh create mode 100755 test/podman-in-podman/test-utils-fedora.sh create mode 100755 test/podman-in-podman/test.sh create mode 100755 test/ubi/test-utils-ubi.sh create mode 100755 test/ubi/test.sh diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml index 7df992da..56be9466 100644 --- a/.github/workflows/test-pr.yaml +++ b/.github/workflows/test-pr.yaml @@ -21,6 +21,9 @@ jobs: docker-outside-of-docker: ./**/docker-outside-of-docker/** docker-outside-of-docker-compose: ./**/docker-outside-of-docker-compose/** docker-in-docker: ./**/docker-in-docker/** + fedora: ./**/fedora/** + ubi: ./**/ubi/** + podman-in-podman: ./**/podman-in-podman/** dotnet: ./**/dotnet/** dotnet-fsharp: ./**/dotnet-fsharp/** dotnet-mssql: ./**/dotnet-mssql/** diff --git a/src/fedora/.devcontainer/Dockerfile b/src/fedora/.devcontainer/Dockerfile new file mode 100644 index 00000000..2efd2a1a --- /dev/null +++ b/src/fedora/.devcontainer/Dockerfile @@ -0,0 +1,27 @@ +# Fedora base image for Dev Containers +# [Choice] Fedora version: 43, 42, 41, latest, rawhide +ARG VARIANT=43 +FROM registry.fedoraproject.org/fedora:${VARIANT} + +# Install base packages needed for Dev Container features +RUN dnf install -y \ + curl \ + wget \ + ca-certificates \ + findutils \ + which \ + tar \ + gzip \ + unzip \ + shadow-utils \ + procps-ng \ + sudo \ + glibc-langpack-en \ + && dnf clean all + +# Set locale to avoid warnings +ENV LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 + +# Note: The common-utils feature will create the vscode user and install additional tools + diff --git a/src/fedora/.devcontainer/devcontainer.json b/src/fedora/.devcontainer/devcontainer.json new file mode 100644 index 00000000..f676903b --- /dev/null +++ b/src/fedora/.devcontainer/devcontainer.json @@ -0,0 +1,40 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/fedora +{ + "name": "Fedora", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VARIANT": "${templateOption:imageVariant}" + } + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // Note: Zsh is installed but Bash remains the default shell (matching official templates) + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "installOhMyZsh": true, + "username": "vscode", + "userUid": "1000", + "userGid": "1000", + "upgradePackages": true + }, + "ghcr.io/devcontainers/features/git:1": {} + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "cat /etc/fedora-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" + "remoteUser": "vscode" +} + + diff --git a/src/fedora/NOTES.md b/src/fedora/NOTES.md new file mode 100644 index 00000000..564bb111 --- /dev/null +++ b/src/fedora/NOTES.md @@ -0,0 +1,44 @@ +## Using this template + +This template creates a Fedora-based development container. Fedora provides cutting-edge packages and is the upstream source for Red Hat Enterprise Linux. + +### Fedora Version Options + +| Version | Description | +|---------|-------------| +| `43` | Fedora 43 (current stable, October 2025) | +| `42` | Fedora 42 (previous stable) | +| `41` | Fedora 41 (extended support) | +| `latest` | Latest stable Fedora release | +| `rawhide` | Development/unstable version | + +### Using with Podman + +This template works well with Podman as the container engine. To configure VS Code to use Podman: + +```json +{ + "dev.containers.dockerPath": "podman" +} +``` + +### Adding Development Tools + +You can add language-specific tools using [Dev Container Features](https://containers.dev/features). For example, to add Python: + +```json +"features": { + "ghcr.io/devcontainers/features/python:1": {} +} +``` + +Or install packages directly using `dnf`: + +```json +"postCreateCommand": "sudo dnf install -y nodejs golang rust" +``` + +### Multi-Architecture Support + +Fedora images are available for both `x86_64` and `aarch64` (ARM64/Apple Silicon). + diff --git a/src/fedora/devcontainer-template.json b/src/fedora/devcontainer-template.json new file mode 100644 index 00000000..48a4f078 --- /dev/null +++ b/src/fedora/devcontainer-template.json @@ -0,0 +1,28 @@ +{ + "id": "fedora", + "version": "1.0.0", + "name": "Fedora", + "description": "Simple Fedora container with Git and common utilities installed.", + "documentationURL": "https://github.com/devcontainers/templates/tree/main/src/fedora", + "publisher": "Dev Container Spec Maintainers", + "licenseURL": "https://github.com/devcontainers/templates/blob/main/LICENSE", + "options": { + "imageVariant": { + "type": "string", + "description": "Fedora version:", + "proposals": [ + "43", + "42", + "41", + "latest", + "rawhide" + ], + "default": "43" + } + }, + "platforms": ["Any"], + "optionalPaths": [ + ".github/*" + ] +} + diff --git a/src/podman-in-podman/.devcontainer/Dockerfile b/src/podman-in-podman/.devcontainer/Dockerfile new file mode 100644 index 00000000..58447fd8 --- /dev/null +++ b/src/podman-in-podman/.devcontainer/Dockerfile @@ -0,0 +1,97 @@ +# Podman-in-Podman for Dev Containers +# Allows running containers inside a dev container using Podman + +# [Choice] Podman version tag: latest or any version (e.g., v5.7.1, v5.7, v5, 5.7.1) +# Version tags use 'v' prefix (e.g., v5.7.1, v5.7, v5) +# The 'v' prefix is optional in input - if you specify '5.7.1', it will be used as 'v5.7.1' +# For best results, specify the full tag with 'v' prefix (e.g., 'v5.7.1', 'v5.7', 'v5') +ARG VARIANT=latest +ARG PODMAN_TAG +ARG INSTALL_BUILDAH="true" +ARG INSTALL_SKOPEO="true" + +# Official Podman images from quay.io +# Tag format: quay.io/podman/stable:latest or quay.io/podman/stable:v5.7.1, v5.7, v5, etc. +# PODMAN_TAG is calculated and passed from devcontainer.json: +# - 'latest' -> 'latest' +# - Other versions -> add 'v' prefix if not present (e.g., '5.7.1' -> 'v5.7.1', 'v5.7.1' -> 'v5.7.1') +# Default to 'latest' if PODMAN_TAG is not provided +FROM quay.io/podman/stable:${PODMAN_TAG:-latest} + +# For official Podman images, Podman is already installed +# Install additional tools and optional components +RUN dnf install -y \ + shadow-utils \ + sudo \ + curl \ + wget \ + ca-certificates \ + findutils \ + which \ + tar \ + gzip \ + unzip \ + procps-ng \ + glibc-langpack-en \ + podman-docker \ + && dnf clean all + +# Install optional tools based on build arguments +# Note: ARG must be redeclared after FROM to be available in this stage +ARG INSTALL_BUILDAH +ARG INSTALL_SKOPEO +RUN if [ "${INSTALL_BUILDAH}" = "true" ]; then \ + dnf install -y buildah && dnf clean all; \ + fi \ + && if [ "${INSTALL_SKOPEO}" = "true" ]; then \ + dnf install -y skopeo && dnf clean all; \ + fi + +# Configure subuid/subgid for rootless containers (will be set up by common-utils feature) +# Using UID/GID 1001 to avoid conflicts with existing users in official Podman image +RUN echo "vscode:100000:65536" >> /etc/subuid \ + && echo "vscode:100000:65536" >> /etc/subgid \ + && echo "1001:100000:65536" >> /etc/subuid \ + && echo "1001:100000:65536" >> /etc/subgid + +# Configure Podman for nested container operation +RUN mkdir -p /etc/containers \ + && echo '[containers]' > /etc/containers/containers.conf \ + && echo 'netns="host"' >> /etc/containers/containers.conf \ + && echo 'userns="host"' >> /etc/containers/containers.conf \ + && echo 'ipcns="host"' >> /etc/containers/containers.conf \ + && echo 'utsns="host"' >> /etc/containers/containers.conf \ + && echo 'cgroupns="host"' >> /etc/containers/containers.conf \ + && echo 'log_driver = "k8s-file"' >> /etc/containers/containers.conf \ + && echo '' >> /etc/containers/containers.conf \ + && echo '[engine]' >> /etc/containers/containers.conf \ + && echo 'cgroup_manager = "cgroupfs"' >> /etc/containers/containers.conf \ + && echo 'events_logger = "file"' >> /etc/containers/containers.conf + +# Configure storage +RUN echo '[storage]' > /etc/containers/storage.conf \ + && echo 'driver = "overlay"' >> /etc/containers/storage.conf \ + && echo '' >> /etc/containers/storage.conf \ + && echo '[storage.options.overlay]' >> /etc/containers/storage.conf \ + && echo 'mount_program = "/usr/bin/fuse-overlayfs"' >> /etc/containers/storage.conf + +# Set locale to avoid warnings +ENV LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 + +# Create /etc/machine-id if it doesn't exist (required by some tools) +# This is a dummy machine-id for container environments (32 hex chars, no hyphens per systemd spec) +RUN if [ ! -f /etc/machine-id ]; then \ + if [ -f /proc/sys/kernel/random/uuid ]; then \ + # Convert UUID format to 32-char hex string (remove hyphens) \ + cat /proc/sys/kernel/random/uuid | tr -d '-' > /etc/machine-id; \ + elif command -v dbus-uuidgen >/dev/null 2>&1; then \ + dbus-uuidgen | tr -d '-' > /etc/machine-id; \ + else \ + # Fallback: generate 32 hex characters \ + od -An -N16 -tx1 /dev/urandom | tr -d ' \n' > /etc/machine-id || \ + echo "00000000000000000000000000000000" > /etc/machine-id; \ + fi; \ + fi + +# Note: The common-utils feature will create the vscode user diff --git a/src/podman-in-podman/.devcontainer/devcontainer.json b/src/podman-in-podman/.devcontainer/devcontainer.json new file mode 100644 index 00000000..13715aee --- /dev/null +++ b/src/podman-in-podman/.devcontainer/devcontainer.json @@ -0,0 +1,65 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/podman-in-podman +{ + "name": "Podman in Podman", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VARIANT": "${templateOption:imageVariant}", + "PODMAN_TAG": "${templateOption:imageVariant}", + "INSTALL_BUILDAH": "${templateOption:installBuildah}", + "INSTALL_SKOPEO": "${templateOption:installSkopeo}" + } + }, + + // Required for nested container operations + "runArgs": [ + "--privileged", + "--security-opt", "label=disable" + ], + + // Persistent storage for container images + "mounts": [ + { + "source": "devcontainer-podman-var-lib-${devcontainerId}", + "target": "/var/lib/containers", + "type": "volume" + } + ], + + // Features to add to the dev container. More info: https://containers.dev/features. + // Note: Using UID/GID 1001 to avoid conflicts with podman user in official image + // Zsh is installed but Bash remains the default shell (matching official templates) + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "installOhMyZsh": true, + "username": "vscode", + "userUid": "1001", + "userGid": "1001", + "upgradePackages": true + }, + "ghcr.io/devcontainers/features/git:1": {} + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Verify Podman is working (run as root for nested container operations) + "postCreateCommand": "sudo podman --version && sudo podman info --format '{{.Host.OCIRuntime.Name}}'", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "redhat.vscode-yaml" + ] + } + }, + + // Using root for nested container operations to avoid user namespace issues + // This is common for Podman-in-Podman scenarios + "remoteUser": "root" +} + + diff --git a/src/podman-in-podman/NOTES.md b/src/podman-in-podman/NOTES.md new file mode 100644 index 00000000..3eed8031 --- /dev/null +++ b/src/podman-in-podman/NOTES.md @@ -0,0 +1,93 @@ +## Using this template + +This template enables running containers inside a dev container using Podman. It's a daemonless alternative to Docker-in-Docker, ideal for container development and CI/CD workflows. + +### What's Included + +- **Podman**: Daemonless container engine, Docker CLI compatible +- **Buildah** (optional): Build OCI container images without a daemon +- **Skopeo** (optional): Inspect and copy container images +- **fuse-overlayfs**: Overlay filesystem for rootless containers +- **slirp4netns**: User-mode networking for rootless containers + +### Podman Version Options + +| Input | Actual Tag | Description | +|-------|------------|-------------| +| `latest` | `latest` | Latest Podman image (default) | +| `5.7.1` | `v5.7.1` | Podman 5.7.1 (full version) | +| `5.7` | `v5.7` | Podman 5.7.x (minor version) | +| `5` | `v5` | Podman 5.x (major version) | +| `v5.7.1` | `v5.7.1` | Podman 5.7.1 (with 'v' prefix) | + +All variants use the [official Podman image](https://quay.io/repository/podman/stable) from `quay.io/podman/stable` with the specified tag format: +- `latest` maps to `quay.io/podman/stable:latest` +- Version tags use the `v` prefix format: `quay.io/podman/stable:v5.7.1`, `v5.7`, `v5`, etc. +- The `v` prefix is optional in input and will be added automatically if missing +- You can use any available version tag from the repository + +### How It Works + +The template configures a privileged container that can run nested containers: + +1. **Privileged mode**: `--privileged` flag allows nested container operations +2. **SELinux disabled**: `--security-opt label=disable` for compatibility +3. **Persistent storage**: Volume mounted at `/var/lib/containers` +4. **Rootless config**: Configured for non-root container operations + +### Example Usage + +```bash +# Run a container +podman run --rm alpine echo "Hello from nested container!" + +# Build an image +podman build -t myapp . + +# Use Docker commands (via podman-docker) +docker ps +docker build -t myapp . +``` + +### Using Buildah + +```bash +# Build from Containerfile +buildah bud -t myapp . + +# Interactive build +container=$(buildah from fedora) +buildah run $container dnf install -y httpd +buildah commit $container myapp +``` + +### Using Skopeo + +```bash +# Inspect remote image +skopeo inspect docker://docker.io/library/alpine:latest + +# Copy image between registries +skopeo copy docker://source/image docker://dest/image +``` + +### Troubleshooting + +**Permission denied errors:** +Ensure the container is running with `--privileged`. The devcontainer.json should include: +```json +"runArgs": ["--privileged", "--security-opt", "label=disable"] +``` + +**Storage issues:** +The template uses a named volume for `/var/lib/containers`. To reset: +```bash +podman volume rm devcontainer-podman-var-lib- +``` + +### Related Resources + +- [Podman Documentation](https://docs.podman.io/) +- [Official Podman Image](https://quay.io/repository/podman/stable) +- [Buildah Documentation](https://buildah.io/) +- [Podman in Podman](https://www.redhat.com/sysadmin/podman-inside-container) diff --git a/src/podman-in-podman/devcontainer-template.json b/src/podman-in-podman/devcontainer-template.json new file mode 100644 index 00000000..30c5d621 --- /dev/null +++ b/src/podman-in-podman/devcontainer-template.json @@ -0,0 +1,33 @@ +{ + "id": "podman-in-podman", + "version": "1.0.0", + "name": "Podman in Podman", + "description": "Create child containers inside a container using Podman. Daemonless alternative to Docker-in-Docker.", + "documentationURL": "https://github.com/devcontainers/templates/tree/main/src/podman-in-podman", + "publisher": "Dev Container Spec Maintainers", + "licenseURL": "https://github.com/devcontainers/templates/blob/main/LICENSE", + "options": { + "imageVariant": { + "type": "string", + "description": "Podman version tag from quay.io/podman/stable. Use 'latest' or any version (e.g., '5.7.1', '5.7', '5', 'v5.7.1'). The 'v' prefix is optional and will be added automatically:", + "proposals": [ + "latest" + ], + "default": "latest" + }, + "installBuildah": { + "type": "boolean", + "description": "Install Buildah for building container images?", + "default": "true" + }, + "installSkopeo": { + "type": "boolean", + "description": "Install Skopeo for image management?", + "default": "true" + } + }, + "platforms": ["Any"], + "optionalPaths": [ + ".github/*" + ] +} diff --git a/src/ubi/.devcontainer/Dockerfile b/src/ubi/.devcontainer/Dockerfile new file mode 100644 index 00000000..a982381d --- /dev/null +++ b/src/ubi/.devcontainer/Dockerfile @@ -0,0 +1,51 @@ +# Red Hat Universal Base Image (UBI) for Dev Containers +# [Choice] UBI version: 10, 9, 8 +ARG UBI_VERSION=10 +# [Choice] Variant: ubi, ubi-minimal, ubi-init +ARG VARIANT=ubi + +FROM registry.access.redhat.com/ubi${UBI_VERSION}/${VARIANT}:latest + +ARG VARIANT + +# Install base packages needed for Dev Container features +# Use microdnf for ubi-minimal, dnf for others +# For UBI 9, curl-minimal is pre-installed and conflicts with curl +# Remove curl-minimal first using rpm (more reliable than microdnf remove) +RUN if [ "${VARIANT}" = "ubi-minimal" ]; then \ + rpm -e --nodeps curl-minimal 2>/dev/null || true; \ + fi + +RUN if [ "${VARIANT}" = "ubi-minimal" ]; then \ + microdnf install -y \ + curl \ + wget \ + ca-certificates \ + findutils \ + which \ + tar \ + gzip \ + unzip \ + shadow-utils \ + procps-ng \ + sudo \ + && microdnf clean all; \ + else \ + # UBI 9 has curl-minimal pre-installed, use --allowerasing to replace with curl + dnf install -y --allowerasing \ + curl \ + wget \ + ca-certificates \ + findutils \ + which \ + tar \ + gzip \ + unzip \ + shadow-utils \ + procps-ng \ + sudo \ + && dnf clean all; \ + fi + +# Note: The common-utils feature will create the vscode user and install additional tools + diff --git a/src/ubi/.devcontainer/devcontainer.json b/src/ubi/.devcontainer/devcontainer.json new file mode 100644 index 00000000..5fef27ce --- /dev/null +++ b/src/ubi/.devcontainer/devcontainer.json @@ -0,0 +1,41 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/ubi +{ + "name": "Red Hat UBI", + "build": { + "dockerfile": "Dockerfile", + "args": { + "UBI_VERSION": "${templateOption:imageVariant}", + "VARIANT": "${templateOption:variant}" + } + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // Note: Zsh is installed but Bash remains the default shell (matching official templates) + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "installOhMyZsh": true, + "username": "vscode", + "userUid": "1000", + "userGid": "1000", + "upgradePackages": true + }, + "ghcr.io/devcontainers/features/git:1": {} + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "cat /etc/redhat-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" + "remoteUser": "vscode" +} + + diff --git a/src/ubi/NOTES.md b/src/ubi/NOTES.md new file mode 100644 index 00000000..6c4f4eab --- /dev/null +++ b/src/ubi/NOTES.md @@ -0,0 +1,55 @@ +## Using this template + +This template creates a development container based on Red Hat Universal Base Image (UBI). UBI provides a freely redistributable, RHEL-compatible base for enterprise development. + +### What is UBI? + +[Universal Base Image (UBI)](https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image) is a freely redistributable subset of Red Hat Enterprise Linux that can be used as a base for containers without requiring a RHEL subscription. + +### UBI Version Options + +| Version | Based On | Support | +|---------|----------|---------| +| `10` | RHEL 10 | Current | +| `9` | RHEL 9 | Maintenance | +| `8` | RHEL 8 | Extended | + +### UBI Variant Options + +| Variant | Package Manager | Description | +|---------|-----------------|-------------| +| `ubi` | dnf | Standard UBI with full package set | +| `ubi-minimal` | microdnf | Reduced footprint, minimal packages | +| `ubi-init` | dnf | Includes systemd for services | + +### Using with Podman + +This template is optimized for use with Podman. Configure VS Code: + +```json +{ + "dev.containers.dockerPath": "podman" +} +``` + +### Adding EPEL Packages + +To access additional packages from EPEL (Extra Packages for Enterprise Linux): + +```json +"postCreateCommand": "sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && sudo dnf install -y " +``` + +### Multi-Architecture Support + +UBI images are available for multiple architectures: +- `x86_64` (amd64) +- `aarch64` (arm64/Apple Silicon) +- `s390x` +- `ppc64le` + +### Official Documentation + +- [Red Hat UBI Documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/building_running_and_managing_containers/assembly_types-of-container-images_building-running-and-managing-containers) +- [UBI Images on Red Hat Container Catalog](https://catalog.redhat.com/software/containers/search?q=ubi) + diff --git a/src/ubi/devcontainer-template.json b/src/ubi/devcontainer-template.json new file mode 100644 index 00000000..92afb71a --- /dev/null +++ b/src/ubi/devcontainer-template.json @@ -0,0 +1,36 @@ +{ + "id": "ubi", + "version": "1.0.0", + "name": "Red Hat Universal Base Image (UBI)", + "description": "Red Hat UBI container for enterprise development. Freely redistributable RHEL-compatible base.", + "documentationURL": "https://github.com/devcontainers/templates/tree/main/src/ubi", + "publisher": "Dev Container Spec Maintainers", + "licenseURL": "https://github.com/devcontainers/templates/blob/main/LICENSE", + "options": { + "imageVariant": { + "type": "string", + "description": "UBI version (RHEL version):", + "proposals": [ + "10", + "9", + "8" + ], + "default": "10" + }, + "variant": { + "type": "string", + "description": "UBI variant:", + "proposals": [ + "ubi", + "ubi-minimal", + "ubi-init" + ], + "default": "ubi" + } + }, + "platforms": ["Any"], + "optionalPaths": [ + ".github/*" + ] +} + diff --git a/test/fedora/test-utils-fedora.sh b/test/fedora/test-utils-fedora.sh new file mode 100755 index 00000000..12233088 --- /dev/null +++ b/test/fedora/test-utils-fedora.sh @@ -0,0 +1,150 @@ +#!/bin/bash +USERNAME=${1:-vscode} + +if [ -z $HOME ]; then + HOME="/root" +fi + +FAILED=() + +echoStderr() +{ + echo "$@" 1>&2 +} + +check() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + if "$@"; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkMultiple() { + PASSED=0 + LABEL="$1" + echo -e "\n🧪 Testing $LABEL." + shift; MINIMUMPASSED=$1 + shift; EXPRESSION="$1" + while [ "$EXPRESSION" != "" ]; do + if $EXPRESSION; then ((PASSED++)); fi + shift; EXPRESSION=$1 + done + if [ $PASSED -ge $MINIMUMPASSED ]; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkOSPackages() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + # Use rpm -q for RPM-based systems (Fedora) + # Check each package individually and count successes + PASSED=0 + TOTAL=0 + for pkg in "$@"; do + TOTAL=$((TOTAL + 1)) + if rpm -q "$pkg" >/dev/null 2>&1; then + PASSED=$((PASSED + 1)) + fi + done + # Require at least 80% of packages to be installed (allowing for optional packages) + if [ $TOTAL -eq 0 ] || [ $((PASSED * 100 / TOTAL)) -ge 80 ]; then + echo "✅ Passed! ($PASSED/$TOTAL packages found)" + return 0 + else + echoStderr "❌ $LABEL check failed. ($PASSED/$TOTAL packages found)" + FAILED+=("$LABEL") + return 1 + fi +} + +checkCommon() +{ + # Fedora-specific package list + PACKAGE_LIST="git \ + openssh-clients \ + less \ + iproute \ + procps-ng \ + curl \ + wget \ + unzip \ + nano \ + jq \ + ca-certificates \ + sudo" + + # Actual tests + checkOSPackages "common-os-packages" ${PACKAGE_LIST} + checkMultiple "vscode-server" 1 "[ -d $HOME/.vscode-server/bin ]" "[ -d $HOME/.vscode-server-insiders/bin ]" "[ -d $HOME/.vscode-test-server/bin ]" "[ -d $HOME/.vscode-remote/bin ]" "[ -d $HOME/.vscode-remote/bin ]" + check "non-root-user" id ${USERNAME} + check "locale" [ $(locale -a | grep -i en_US.utf8) ] + check "sudo" sudo echo "sudo works." + check "zsh" zsh --version + check "oh-my-zsh" [ -d "$HOME/.oh-my-zsh" ] + check "login-shell-path" [ -f "/etc/profile.d/00-restore-env.sh" ] + check "code" which code +} + +reportResults() { + if [ ${#FAILED[@]} -ne 0 ]; then + echoStderr -e "\n💥 Failed tests: ${FAILED[@]}" + exit 1 + else + echo -e "\n💯 All passed!" + exit 0 + fi +} + +# Useful for scenarios where UID/GID is not automatically updated - happens in GitHub Actions w/Docker Compose +fixTestProjectFolderPrivs() { + if [ "${USERNAME}" != "root" ]; then + TEST_PROJECT_FOLDER="${1:-$SCRIPT_FOLDER}" + FOLDER_USER="$(stat -c '%U' "${TEST_PROJECT_FOLDER}")" + if [ "${FOLDER_USER}" != "${USERNAME}" ]; then + echoStderr "WARNING: Test project folder is owned by ${FOLDER_USER}. Updating to ${USERNAME}." + sudo chown -R ${USERNAME} "${TEST_PROJECT_FOLDER}" + fi + fi +} + +checkExtension() { + # Happens asynchronusly, so keep retrying 10 times with an increasing delay + EXTN_ID="$1" + TIMEOUT_SECONDS="${2:-10}" + RETRY_COUNT=0 + echo -e -n "\n🧪 Looking for extension $1 for maximum of ${TIMEOUT_SECONDS}s" + until [ "${RETRY_COUNT}" -eq "${TIMEOUT_SECONDS}" ] || \ + [ ! -e $HOME/.vscode-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-server-insiders/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-test-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-remote/extensions/${EXTN_ID}* ] + do + sleep 1s + (( RETRY_COUNT++ )) + echo -n "." + done + + if [ ${RETRY_COUNT} -lt ${TIMEOUT_SECONDS} ]; then + echo -e "\n✅ Passed!" + return 0 + else + echoStderr -e "\n❌ Extension $EXTN_ID not found." + FAILED+=("$LABEL") + return 1 + fi +} + diff --git a/test/fedora/test.sh b/test/fedora/test.sh new file mode 100755 index 00000000..142dc647 --- /dev/null +++ b/test/fedora/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash +cd $(dirname "$0") + +source test-utils-fedora.sh vscode + +# Run common tests +checkCommon + +# Fedora-specific tests +check "fedora-release" cat /etc/fedora-release + +# Report result +reportResults diff --git a/test/podman-in-podman/test-utils-fedora.sh b/test/podman-in-podman/test-utils-fedora.sh new file mode 100755 index 00000000..12233088 --- /dev/null +++ b/test/podman-in-podman/test-utils-fedora.sh @@ -0,0 +1,150 @@ +#!/bin/bash +USERNAME=${1:-vscode} + +if [ -z $HOME ]; then + HOME="/root" +fi + +FAILED=() + +echoStderr() +{ + echo "$@" 1>&2 +} + +check() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + if "$@"; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkMultiple() { + PASSED=0 + LABEL="$1" + echo -e "\n🧪 Testing $LABEL." + shift; MINIMUMPASSED=$1 + shift; EXPRESSION="$1" + while [ "$EXPRESSION" != "" ]; do + if $EXPRESSION; then ((PASSED++)); fi + shift; EXPRESSION=$1 + done + if [ $PASSED -ge $MINIMUMPASSED ]; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkOSPackages() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + # Use rpm -q for RPM-based systems (Fedora) + # Check each package individually and count successes + PASSED=0 + TOTAL=0 + for pkg in "$@"; do + TOTAL=$((TOTAL + 1)) + if rpm -q "$pkg" >/dev/null 2>&1; then + PASSED=$((PASSED + 1)) + fi + done + # Require at least 80% of packages to be installed (allowing for optional packages) + if [ $TOTAL -eq 0 ] || [ $((PASSED * 100 / TOTAL)) -ge 80 ]; then + echo "✅ Passed! ($PASSED/$TOTAL packages found)" + return 0 + else + echoStderr "❌ $LABEL check failed. ($PASSED/$TOTAL packages found)" + FAILED+=("$LABEL") + return 1 + fi +} + +checkCommon() +{ + # Fedora-specific package list + PACKAGE_LIST="git \ + openssh-clients \ + less \ + iproute \ + procps-ng \ + curl \ + wget \ + unzip \ + nano \ + jq \ + ca-certificates \ + sudo" + + # Actual tests + checkOSPackages "common-os-packages" ${PACKAGE_LIST} + checkMultiple "vscode-server" 1 "[ -d $HOME/.vscode-server/bin ]" "[ -d $HOME/.vscode-server-insiders/bin ]" "[ -d $HOME/.vscode-test-server/bin ]" "[ -d $HOME/.vscode-remote/bin ]" "[ -d $HOME/.vscode-remote/bin ]" + check "non-root-user" id ${USERNAME} + check "locale" [ $(locale -a | grep -i en_US.utf8) ] + check "sudo" sudo echo "sudo works." + check "zsh" zsh --version + check "oh-my-zsh" [ -d "$HOME/.oh-my-zsh" ] + check "login-shell-path" [ -f "/etc/profile.d/00-restore-env.sh" ] + check "code" which code +} + +reportResults() { + if [ ${#FAILED[@]} -ne 0 ]; then + echoStderr -e "\n💥 Failed tests: ${FAILED[@]}" + exit 1 + else + echo -e "\n💯 All passed!" + exit 0 + fi +} + +# Useful for scenarios where UID/GID is not automatically updated - happens in GitHub Actions w/Docker Compose +fixTestProjectFolderPrivs() { + if [ "${USERNAME}" != "root" ]; then + TEST_PROJECT_FOLDER="${1:-$SCRIPT_FOLDER}" + FOLDER_USER="$(stat -c '%U' "${TEST_PROJECT_FOLDER}")" + if [ "${FOLDER_USER}" != "${USERNAME}" ]; then + echoStderr "WARNING: Test project folder is owned by ${FOLDER_USER}. Updating to ${USERNAME}." + sudo chown -R ${USERNAME} "${TEST_PROJECT_FOLDER}" + fi + fi +} + +checkExtension() { + # Happens asynchronusly, so keep retrying 10 times with an increasing delay + EXTN_ID="$1" + TIMEOUT_SECONDS="${2:-10}" + RETRY_COUNT=0 + echo -e -n "\n🧪 Looking for extension $1 for maximum of ${TIMEOUT_SECONDS}s" + until [ "${RETRY_COUNT}" -eq "${TIMEOUT_SECONDS}" ] || \ + [ ! -e $HOME/.vscode-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-server-insiders/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-test-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-remote/extensions/${EXTN_ID}* ] + do + sleep 1s + (( RETRY_COUNT++ )) + echo -n "." + done + + if [ ${RETRY_COUNT} -lt ${TIMEOUT_SECONDS} ]; then + echo -e "\n✅ Passed!" + return 0 + else + echoStderr -e "\n❌ Extension $EXTN_ID not found." + FAILED+=("$LABEL") + return 1 + fi +} + diff --git a/test/podman-in-podman/test.sh b/test/podman-in-podman/test.sh new file mode 100755 index 00000000..9b651bf6 --- /dev/null +++ b/test/podman-in-podman/test.sh @@ -0,0 +1,42 @@ +#!/bin/bash +cd $(dirname "$0") + +source test-utils-fedora.sh root + +# Run common tests +checkCommon + +# Podman-specific tests +check "podman-installed" which podman +check "podman-version" podman --version +check "podman-info" podman info + +# Check nested container operation +echo -e "\n🧪 Testing nested container (podman run)" +if podman run --rm alpine:latest echo "Hello from nested container"; then + echo "✅ Passed!" +else + echoStderr "❌ Nested container check failed." + FAILED+=("nested-container") +fi + +# Check optional tools (don't fail if not installed) +checkOptional() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL (optional)" + if "$@"; then + echo "✅ Passed!" + return 0 + else + echoStderr "⚠️ $LABEL not available (optional)" + return 0 # Don't fail for optional checks + fi +} + +checkOptional "buildah" buildah --version +checkOptional "skopeo" skopeo --version +checkOptional "docker-alias" docker --version + +# Report result +reportResults diff --git a/test/ubi/test-utils-ubi.sh b/test/ubi/test-utils-ubi.sh new file mode 100755 index 00000000..1d87ae40 --- /dev/null +++ b/test/ubi/test-utils-ubi.sh @@ -0,0 +1,150 @@ +#!/bin/bash +USERNAME=${1:-vscode} + +if [ -z $HOME ]; then + HOME="/root" +fi + +FAILED=() + +echoStderr() +{ + echo "$@" 1>&2 +} + +check() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + if "$@"; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkMultiple() { + PASSED=0 + LABEL="$1" + echo -e "\n🧪 Testing $LABEL." + shift; MINIMUMPASSED=$1 + shift; EXPRESSION="$1" + while [ "$EXPRESSION" != "" ]; do + if $EXPRESSION; then ((PASSED++)); fi + shift; EXPRESSION=$1 + done + if [ $PASSED -ge $MINIMUMPASSED ]; then + echo "✅ Passed!" + return 0 + else + echoStderr "❌ $LABEL check failed." + FAILED+=("$LABEL") + return 1 + fi +} + +checkOSPackages() { + LABEL=$1 + shift + echo -e "\n🧪 Testing $LABEL" + # Use rpm -q for RPM-based systems (UBI/RHEL) + # Check each package individually and count successes + PASSED=0 + TOTAL=0 + for pkg in "$@"; do + TOTAL=$((TOTAL + 1)) + if rpm -q "$pkg" >/dev/null 2>&1; then + PASSED=$((PASSED + 1)) + fi + done + # Require at least 80% of packages to be installed (allowing for optional packages) + if [ $TOTAL -eq 0 ] || [ $((PASSED * 100 / TOTAL)) -ge 80 ]; then + echo "✅ Passed! ($PASSED/$TOTAL packages found)" + return 0 + else + echoStderr "❌ $LABEL check failed. ($PASSED/$TOTAL packages found)" + FAILED+=("$LABEL") + return 1 + fi +} + +checkCommon() +{ + # UBI/RHEL-specific package list + PACKAGE_LIST="git \ + openssh-clients \ + less \ + iproute \ + procps-ng \ + curl \ + wget \ + unzip \ + nano \ + jq \ + ca-certificates \ + sudo" + + # Actual tests + checkOSPackages "common-os-packages" ${PACKAGE_LIST} + checkMultiple "vscode-server" 1 "[ -d $HOME/.vscode-server/bin ]" "[ -d $HOME/.vscode-server-insiders/bin ]" "[ -d $HOME/.vscode-test-server/bin ]" "[ -d $HOME/.vscode-remote/bin ]" "[ -d $HOME/.vscode-remote/bin ]" + check "non-root-user" id ${USERNAME} + check "locale" [ $(locale -a | grep -iE "(en_US|C\.UTF)" | head -1) ] + check "sudo" sudo echo "sudo works." + check "zsh" zsh --version + check "oh-my-zsh" [ -d "$HOME/.oh-my-zsh" ] + check "login-shell-path" [ -f "/etc/profile.d/00-restore-env.sh" ] + check "code" which code +} + +reportResults() { + if [ ${#FAILED[@]} -ne 0 ]; then + echoStderr -e "\n💥 Failed tests: ${FAILED[@]}" + exit 1 + else + echo -e "\n💯 All passed!" + exit 0 + fi +} + +# Useful for scenarios where UID/GID is not automatically updated - happens in GitHub Actions w/Docker Compose +fixTestProjectFolderPrivs() { + if [ "${USERNAME}" != "root" ]; then + TEST_PROJECT_FOLDER="${1:-$SCRIPT_FOLDER}" + FOLDER_USER="$(stat -c '%U' "${TEST_PROJECT_FOLDER}")" + if [ "${FOLDER_USER}" != "${USERNAME}" ]; then + echoStderr "WARNING: Test project folder is owned by ${FOLDER_USER}. Updating to ${USERNAME}." + sudo chown -R ${USERNAME} "${TEST_PROJECT_FOLDER}" + fi + fi +} + +checkExtension() { + # Happens asynchronusly, so keep retrying 10 times with an increasing delay + EXTN_ID="$1" + TIMEOUT_SECONDS="${2:-10}" + RETRY_COUNT=0 + echo -e -n "\n🧪 Looking for extension $1 for maximum of ${TIMEOUT_SECONDS}s" + until [ "${RETRY_COUNT}" -eq "${TIMEOUT_SECONDS}" ] || \ + [ ! -e $HOME/.vscode-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-server-insiders/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-test-server/extensions/${EXTN_ID}* ] || \ + [ ! -e $HOME/.vscode-remote/extensions/${EXTN_ID}* ] + do + sleep 1s + (( RETRY_COUNT++ )) + echo -n "." + done + + if [ ${RETRY_COUNT} -lt ${TIMEOUT_SECONDS} ]; then + echo -e "\n✅ Passed!" + return 0 + else + echoStderr -e "\n❌ Extension $EXTN_ID not found." + FAILED+=("$LABEL") + return 1 + fi +} + diff --git a/test/ubi/test.sh b/test/ubi/test.sh new file mode 100755 index 00000000..28531513 --- /dev/null +++ b/test/ubi/test.sh @@ -0,0 +1,14 @@ +#!/bin/bash +cd $(dirname "$0") + +source test-utils-ubi.sh vscode + +# Run common tests +checkCommon + +# UBI-specific tests +check "redhat-release" cat /etc/redhat-release +check "ubi-image" grep -q "Red Hat" /etc/redhat-release + +# Report result +reportResults