diff --git a/.github/contributors.yaml b/.github/contributors.yaml index d8f4a6b20..fc07bbea2 100644 --- a/.github/contributors.yaml +++ b/.github/contributors.yaml @@ -56,7 +56,7 @@ users: hemang1404: name: Hemang Sharrma email: hemangsharrma@gmail.com - jiwahn: + ilp-sys: name: Jiwoo Ahn email: ikwydls1314@gmail.com goyalpalak18: @@ -86,12 +86,3 @@ users: kaizakin: name: Karthik Balasubramanian email: karthikbalasubramanian08@gmail.com - MUFFANUJ: - name: Anuj Kumar Singh - email: anujsinghhero292@gmail.com - neo-0007: - name: Hrishikesh Gohain - email: hrishikeshgohain123@gmail.com - rishi-jat: - name: Rishi Jat - email: rishijat098@gmail.com diff --git a/.github/linters/urunc-dict.txt b/.github/linters/urunc-dict.txt index bf838cdfe..538649e07 100644 --- a/.github/linters/urunc-dict.txt +++ b/.github/linters/urunc-dict.txt @@ -88,7 +88,6 @@ TUNTAP Timestamping Tmpfs Tuntap -TTRPC UNBINDABLE URUNC Unikernel @@ -128,11 +127,8 @@ clis cmainas cmdline cntr -containerdshim containerd containerd's -containersapi -contentapi cpio creack crictl @@ -154,7 +150,6 @@ elevateprivileges elfloader endmacro epoll -errdefs etest etesting etfs @@ -182,7 +177,6 @@ httpreply iface ifaces ifname -imagesapi initpipe initrds inlinehilite @@ -204,7 +198,6 @@ kwds lazytime lenag lenpg -leasesapi levarage libc libcontainer @@ -303,10 +296,8 @@ setgroup settime sgid sharedfs -snapshotsapi sigaction sigreturn -sigstr sirupsen socker stretchr @@ -326,7 +317,6 @@ tmpl tomlq traefik triger -ttrpc twemoji uidmap ukernel diff --git a/.github/workflows/add-git-trailers.yml b/.github/workflows/add-git-trailers.yml index 59d8b51bf..73d5f5ffa 100644 --- a/.github/workflows/add-git-trailers.yml +++ b/.github/workflows/add-git-trailers.yml @@ -1,12 +1,12 @@ name: Add Git Trailers to PR commits on: - pull_request_review: - types: [submitted] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + workflow_call: + secrets: + GIT_CLONE_PAT: + required: false + URUNC_BOT_PRIVATE_KEY: + required: true permissions: contents: read @@ -14,12 +14,16 @@ permissions: jobs: git-trailers: name: Add Git Trailers - if: >- - github.event.pull_request.base.ref == 'main' && - github.event.review.state == 'approved' - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.runner }} + strategy: + matrix: + include: + - arch: amd64 + runner: ubuntu-22.04 + continue-on-error: true permissions: contents: write + pull-requests: write steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 @@ -36,6 +40,11 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} + - name: Append git trailers + uses: nubificus/git-trailers@8e08c91bb4c1fd9cb1ccbd9cc8029c31acf8da66 # feat_use_rebase + with: + user_info: .github/contributors.yaml + - name: Generate urunc-bot token id: generate-token uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 @@ -43,16 +52,20 @@ jobs: app-id: ${{ vars.URUNC_BOT_APP_ID }} private-key: ${{ secrets.URUNC_BOT_PRIVATE_KEY }} + - name: Set up Git + run: | + git config --global user.name "urunc-bot[bot]" + git config --global user.email "urunc-bot[bot]@users.noreply.github.com" + - name: Append git trailers - uses: nubificus/git-trailers@1d1595aacfd9239ae69d773cb895606daa17e538 + uses: nubificus/git-trailers@18fd322f3fbfd505b4de728974a4ac1f32f758a7 # feat_auto_merge with: - token: ${{ steps.generate-token.outputs.token }} - user-info: .github/contributors.yaml + user_info: .github/contributors.yaml - name: Merge PR env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} - PR_URL: ${{ github.event.pull_request.html_url }} run: | - sleep 5 # Wait for github to get updated with the push. Otherwise merge will fail + PR_URL=${{ github.event.pull_request.html_url }} + gh pr merge "$PR_URL" --rebase --admin diff --git a/.github/workflows/build-latest.yml b/.github/workflows/build-latest.yml index eb8b6b5a0..841e8a341 100644 --- a/.github/workflows/build-latest.yml +++ b/.github/workflows/build-latest.yml @@ -72,7 +72,7 @@ jobs: - name: Extract Docker metadata id: meta - uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 with: images: ${{ env.IMAGE_NAME }} tags: | @@ -110,7 +110,7 @@ jobs: - name: Build and push urunc-deploy-${{ matrix.arch}} id: build-and-push - uses: docker/build-push-action@789f68658055d3ca993799b232b5c46dfe3f114d # master + uses: docker/build-push-action@9e436ba9f2d7bcd1d038c8e55d039d37896ddc5d # master with: context: ./deployment/urunc-deploy tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/ci_on_push.yml b/.github/workflows/ci_on_push.yml index af46b1524..5b8827bf2 100644 --- a/.github/workflows/ci_on_push.yml +++ b/.github/workflows/ci_on_push.yml @@ -1,6 +1,8 @@ name: urunc CI on: + push: + branches: ["chore/issue-208-e2e-multi-distro"] pull_request: branches: ["main"] types: [opened,synchronize,reopened,labeled] diff --git a/.github/workflows/ci_test.yml b/.github/workflows/ci_test.yml new file mode 100644 index 000000000..c33c1b639 --- /dev/null +++ b/.github/workflows/ci_test.yml @@ -0,0 +1,20 @@ +name: urunc CI Test + +on: + push: + branches: ["chore/issue-208-e2e-multi-distro"] + +jobs: + ci-test: + permissions: + contents: read + packages: write + id-token: write + attestations: write + pull-requests: read + uses: ./.github/workflows/ci.yml + with: + ref: ${{ github.sha }} + skip-build: "no" + skip-lint: "no" + secrets: inherit diff --git a/.github/workflows/pr-merge.yml b/.github/workflows/pr-merge.yml index fa66f2f90..1b57dfd40 100644 --- a/.github/workflows/pr-merge.yml +++ b/.github/workflows/pr-merge.yml @@ -4,8 +4,6 @@ on: pull_request_target: types: - closed - branches: - - 'main-pr*' permissions: contents: read @@ -13,19 +11,22 @@ permissions: jobs: add-trailers-and-merge: if: | - github.event.pull_request.merged == true + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.base.ref, 'main-pr') runs-on: ubuntu-latest permissions: contents: write + steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 with: egress-policy: audit - - name: Exit if PR is not rebaseable - if: ${{ github.event.pull_request.rebaseable != null && github.event.pull_request.rebaseable == false }} - run: exit 1 + - name: Set up Git + run: | + git config --global user.name "urunc-bot[bot]" + git config --global user.email "urunc-bot[bot]@users.noreply.github.com" - name: Check out repo uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -41,18 +42,16 @@ jobs: private-key: ${{ secrets.URUNC_BOT_PRIVATE_KEY }} - name: Append git trailers - uses: nubificus/git-trailers@1d1595aacfd9239ae69d773cb895606daa17e538 + uses: nubificus/git-trailers@18fd322f3fbfd505b4de728974a4ac1f32f758a7 # feat_auto_merge with: - token: ${{ steps.generate-token.outputs.token }} - user-info: .github/contributors.yaml + user_info: .github/contributors.yaml - name: Create a Pull Request from PR_BRANCH to main and merge it env: GH_TOKEN: ${{ steps.generate-token.outputs.token }} - PR_BRANCH: ${{ github.event.pull_request.base.ref }} run: | PR_BRANCH=${{ github.event.pull_request.base.ref }} - + # Create the pull request PR_URL=$(gh pr create \ --head "$PR_BRANCH" \ diff --git a/.github/workflows/pr-trailers.yml b/.github/workflows/pr-trailers.yml new file mode 100644 index 000000000..82fbcde7f --- /dev/null +++ b/.github/workflows/pr-trailers.yml @@ -0,0 +1,16 @@ +name: Add Git Trailers to PR commits + +on: + pull_request_review: + types: [submitted] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + git-trailers: + name: Add Git Trailers to PR commits + if: ${{ github.event.pull_request.base.ref == 'main' && github.event.review.state == 'approved' }} + uses: ./.github/workflows/add-git-trailers.yml + secrets: inherit diff --git a/.github/workflows/release-trigger.yaml b/.github/workflows/release-trigger.yaml index 4b79a5c87..9db764ecf 100644 --- a/.github/workflows/release-trigger.yaml +++ b/.github/workflows/release-trigger.yaml @@ -159,7 +159,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Create initial release - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 id: create_release with: files: | diff --git a/.github/workflows/vm_test.yml b/.github/workflows/vm_test.yml index 7ea39c82a..23e7a243f 100644 --- a/.github/workflows/vm_test.yml +++ b/.github/workflows/vm_test.yml @@ -54,263 +54,231 @@ jobs: prepare: name: VM test runs-on: ${{ matrix.runner }} + container: + image: ${{ matrix.container_image }} + options: --privileged strategy: + fail-fast: false matrix: arch: ${{ fromJSON(inputs.runner-archs) }} test: ["test_ctr","test_nerdctl","test_crictl","test_docker"] + distro: [ubuntu, fedora, rockylinux, opensuse] include: - arch: amd64 runner: ubuntu-22.04 - arch: arm64 runner: ubuntu-22.04-arm + - distro: ubuntu + container_image: ubuntu:22.04 + - distro: fedora + container_image: fedora:latest + - distro: rockylinux + container_image: rockylinux:9 + - distro: opensuse + container_image: opensuse/leap:latest steps: - - name: Validate inputs (prevent command injection) - shell: bash - env: - GO_VERSION: ${{ inputs.go_version }} - RUNC_VERSION: ${{ inputs.runc_version }} - CONTAINERD_VERSION: ${{ inputs.containerd_version }} - CNI_VERSION: ${{ inputs.cni_version }} - NERDCTL_VERSION: ${{ inputs.nerdctl_version }} - CRICTL_VERSION: ${{ inputs.crictl_version }} - FIRECRACKER_VERSION: ${{ inputs.firecracker_version }} - CLOUD_HYPERVISOR_VERSION: ${{ inputs.cloud_hypervisor_version }} - SOLO5_VERSION: ${{ inputs.solo5_version }} - run: | - SAFE_GO_VERSION="$GO_VERSION" - SAFE_RUNC_VERSION="$RUNC_VERSION" - SAFE_CONTAINERD_VERSION="$CONTAINERD_VERSION" - SAFE_CNI_VERSION="$CNI_VERSION" - SAFE_NERDCTL_VERSION="$NERDCTL_VERSION" - SAFE_CRICTL_VERSION="$CRICTL_VERSION" - SAFE_FIRECRACKER_VERSION="$FIRECRACKER_VERSION" - SAFE_CLOUD_HYPERVISOR_VERSION="$CLOUD_HYPERVISOR_VERSION" - SAFE_SOLO5_VERSION="$SOLO5_VERSION" - - for var in SAFE_GO_VERSION SAFE_RUNC_VERSION SAFE_CONTAINERD_VERSION SAFE_CNI_VERSION SAFE_NERDCTL_VERSION SAFE_CRICTL_VERSION SAFE_FIRECRACKER_VERSION SAFE_CLOUD_HYPERVISOR_VERSION SAFE_SOLO5_VERSION; do - value="${!var}" - if ! [[ "$value" =~ ^v?[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then - echo "Invalid format for $var: $value" + - name: Validate inputs (prevent command injection) + shell: bash + env: + ARCH: ${{ matrix.arch }} + TEST: ${{ matrix.test }} + DISTRO: ${{ matrix.distro }} + run: | + case "$ARCH" in + amd64|arm64|arm) ;; + *) echo "Invalid arch"; exit 1 ;; + esac + case "$TEST" in + test_ctr|test_nerdctl|test_crictl|test_docker) ;; + *) echo "Invalid test"; exit 1 ;; + esac + case "$DISTRO" in + ubuntu|fedora|rockylinux|opensuse) ;; + *) echo "Invalid distro"; exit 1 ;; + esac + + - name: Pre-checkout dependencies + run: | + if command -v zypper >/dev/null; then + zypper install -y tar gzip + fi + + - uses: actions/checkout@v4 + + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.go_version }} + cache: false + + - name: Install base dependencies + run: | + if command -v apt-get >/dev/null; then + apt-get update + apt-get install -y git wget curl build-essential libseccomp-dev pkg-config bc make qemu-system e2fsprogs ca-certificates tar + elif command -v dnf >/dev/null; then + dnf install -y --allowerasing git wget curl gcc gcc-c++ make libseccomp-devel pkgconfig bc e2fsprogs ca-certificates tar + dnf install -y qemu-system-x86 || dnf install -y qemu-kvm || true + elif command -v zypper >/dev/null; then + zypper --non-interactive install -y git wget curl gcc gcc-c++ make libseccomp-devel pkg-config bc qemu-x86 e2fsprogs ca-certificates tar || \ + zypper --non-interactive install -y git wget curl gcc gcc-c++ make libseccomp2-devel pkg-config bc qemu-x86 e2fsprogs ca-certificates tar + else + echo "Unsupported distribution" exit 1 fi - done - - - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 - with: - egress-policy: audit - - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - with: - go-version: ${{ inputs.go_version }} - cache: false - - - name: Install base dependencies - run: | - sudo apt-get update - sudo apt-get install -y git wget build-essential libseccomp-dev pkg-config bc make qemu-system - wget https://s3.nbfc.io/nbfc-assets/github/urunc/bin/virtiofsd - sudo chmod +x virtiofsd - sudo mv virtiofsd /usr/libexec/virtiofsd - - - name: Install runc - env: - RUNC_VERSION: ${{ inputs.runc_version }} - run: | - SAFE_RUNC="${RUNC_VERSION}" - wget -q "https://github.com/opencontainers/runc/releases/download/v${SAFE_RUNC}/runc.$(dpkg --print-architecture)" - sudo install -m 755 runc.$(dpkg --print-architecture) /usr/local/sbin/runc - rm -f ./runc.$(dpkg --print-architecture) - - - name: Install containerd - env: - CONTAINERD_VERSION: ${{ inputs.containerd_version }} - run: | - SAFE_CONTAINERD="${CONTAINERD_VERSION}" - wget -q "https://github.com/containerd/containerd/releases/download/v${SAFE_CONTAINERD}/containerd-${SAFE_CONTAINERD}-linux-$(dpkg --print-architecture).tar.gz" - sudo tar Cxzvf /usr/local "containerd-${SAFE_CONTAINERD}-linux-$(dpkg --print-architecture).tar.gz" - rm -f "containerd-${SAFE_CONTAINERD}-linux-$(dpkg --print-architecture).tar.gz" - - - name: Set up containerd service - env: - CONTAINERD_VERSION: ${{ inputs.containerd_version }} - run: | - SAFE_CONTAINERD="$CONTAINERD_VERSION" - wget -q "https://raw.githubusercontent.com/containerd/containerd/v${SAFE_CONTAINERD}/containerd.service" - sudo rm -f /lib/systemd/system/containerd.service - sudo mv containerd.service /lib/systemd/system/containerd.service - sudo systemctl daemon-reload - sudo systemctl enable --now containerd - - - name: Configure containerd - run: | - sudo mkdir -p /etc/containerd/ - sudo mv /etc/containerd/config.toml /etc/containerd/config.toml.bak || true - sudo containerd config default | sudo tee /etc/containerd/config.toml - sudo systemctl restart containerd - - - name: Setup devmapper - run: | - sudo mkdir -p /usr/local/bin/scripts - sudo cp script/dm_create.sh /usr/local/bin/scripts/dm_create.sh - sudo chmod 755 /usr/local/bin/scripts/dm_create.sh - sudo /usr/local/bin/scripts/dm_create.sh - sudo sed -i "/\[plugins\.'io\.containerd\.snapshotter\.v1\.devmapper'\]/,/^$/d" /etc/containerd/config.toml - sudo tee -a /etc/containerd/config.toml > /dev/null <<'EOT' - [plugins.'io.containerd.snapshotter.v1.devmapper'] - pool_name = "containerd-pool" - root_path = "/var/lib/containerd/io.containerd.snapshotter.v1.devmapper" - base_image_size = "10GB" - fs_type = "ext2" - EOT - sudo tee -a /etc/containerd/config.toml > /dev/null < /dev/null <<'EOT' - runtime-endpoint: unix:///run/containerd/containerd.sock - image-endpoint: unix:///run/containerd/containerd.sock - timeout: 20 - EOT - - - name: Install Firecracker - env: - FC_VERSION: ${{ inputs.firecracker_version }} - run: | - SAFE_FC="${FC_VERSION}" - ARCH="$(uname -m)" - release_url="https://github.com/firecracker-microvm/firecracker/releases" - curl -L "${release_url}/download/${SAFE_FC}/firecracker-${SAFE_FC}-${ARCH}.tgz" | tar -xz - sudo mv "release-${SAFE_FC}-${ARCH}/firecracker-${SAFE_FC}-${ARCH}" /usr/local/bin/firecracker - rm -fr "release-${SAFE_FC}-${ARCH}" - - - name: Install Cloud Hypervisor - env: - CLOUD_HYPERVISOR_VERSION: ${{ inputs.cloud_hypervisor_version }} - run: | - SAFE_CLOUD_HYPERVISOR="${CLOUD_HYPERVISOR_VERSION}" - curl -sL "https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/${SAFE_CLOUD_HYPERVISOR}/cloud-hypervisor-static" -o cloud-hypervisor - chmod +x cloud-hypervisor - sudo mv cloud-hypervisor /usr/local/bin/cloud-hypervisor - - - name: Install solo5 - env: - SOLO5_VERSION: ${{ inputs.solo5_version }} - run: | - SAFE_SOLO5="${SOLO5_VERSION}" - git clone -b "${SAFE_SOLO5}" https://github.com/Solo5/solo5.git - cd solo5 - ./configure.sh && make -j$(nproc) - sudo cp tenders/hvt/solo5-hvt /usr/local/bin - sudo cp tenders/spt/solo5-spt /usr/local/bin - - - name: Download urunc artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: urunc_static_${{ matrix.arch }}-${{ github.run_id }} - path: ./ - - - name: Download containerd-shim-urunc-v2 artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: containerd-shim-urunc-v2_static_${{ matrix.arch }}-${{ github.run_id }} - path: ./ - - - name: Install urunc - id: install-urunc - run: | - chmod +x urunc_static_${{ matrix.arch }} - chmod +x containerd-shim-urunc-v2_static_${{ matrix.arch }} - sudo mv urunc_static_${{ matrix.arch }} /usr/local/bin/urunc - sudo mv containerd-shim-urunc-v2_static_${{ matrix.arch }} /usr/local/bin/containerd-shim-urunc-v2 - urunc --version - - - name: Configure urunc with debug logging - run: | - sudo mkdir -p /etc/urunc - sudo tee /etc/urunc/config.toml > /dev/null <<'EOF' - [log] - level = "debug" - syslog = true - EOF - - - name: Add runner user to KVM group - if: ${{ matrix.arch == 'amd64' }} - id: kvm-setup - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - sudo usermod -a -G kvm $USER - - - name: Prepare urunc folder - id: prepare - run: | - export GOROOT=$(go env GOROOT) - export PATH="$GOROOT/bin:$PATH" - go version - go env GOROOT - make prepare - - - name: Run ${{ matrix.test }} - id: test - run: | - # Set up Go environment properly - export GOROOT=$(go env GOROOT) - export PATH="$GOROOT/bin:$PATH" - go version - go env GOROOT - if [ "${{ matrix.arch }}" = "arm64" ]; then - sudo -E env "PATH=$PATH" "GOROOT=$GOROOT" make ${{ matrix.test }}_Spt - else - sudo -E env "PATH=$PATH" "GOROOT=$GOROOT" make ${{ matrix.test }} - fi - - - name: Dump urunc logs on failure - if: failure() - run: | - echo "=== urunc debug logs ===" - sudo journalctl --identifier=urunc --no-pager + wget https://s3.nbfc.io/nbfc-assets/github/urunc/bin/virtiofsd + chmod +x virtiofsd + mv virtiofsd /usr/libexec/virtiofsd + + - name: Install runc + run: | + RUNC_VERSION="${{ inputs.runc_version }}" + # Ensure version has 'v' prefix + [[ "$RUNC_VERSION" == v* ]] || RUNC_VERSION="v${RUNC_VERSION}" + wget "https://github.com/opencontainers/runc/releases/download/${RUNC_VERSION}/runc.${{ matrix.arch }}" + install -m 755 runc.${{ matrix.arch }} /usr/local/sbin/runc + + - name: Install containerd + run: | + wget https://github.com/containerd/containerd/releases/download/v${{ inputs.containerd_version }}/containerd-${{ inputs.containerd_version }}-linux-${{ matrix.arch }}.tar.gz + tar Cxzvf /usr/local containerd-${{ inputs.containerd_version }}-linux-${{ matrix.arch }}.tar.gz + + - name: Set up containerd service + run: | + mkdir -p /usr/local/lib/systemd/system + wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -O /usr/local/lib/systemd/system/containerd.service + systemctl daemon-reload + systemctl enable --now containerd + + - name: Configure containerd + run: | + mkdir -p /etc/containerd + containerd config default | tee /etc/containerd/config.toml + + - name: Restart containerd + run: | + systemctl restart containerd + + - name: Setup devmapper + run: | + modprobe dm_thin_pool + mkdir -p /var/lib/containerd/devmapper + # Use a loop device for devmapper in CI + dd if=/dev/zero of=/var/lib/containerd/devmapper/data bs=1M count=1024 + dd if=/dev/zero of=/var/lib/containerd/devmapper/metadata bs=1M count=128 + DATA_DEV=$(losetup -f --show /var/lib/containerd/devmapper/data) + META_DEV=$(losetup -f --show /var/lib/containerd/devmapper/metadata) + dmsetup create containerd-pool --table "0 2097152 thin-pool $META_DEV $DATA_DEV 128 32768" + + - name: Configure containerd for devmapper and urunc + run: | + sed -i "/\[plugins\.'io\.containerd\.snapshotter\.v1\.devmapper'\]/,/^$/d" /etc/containerd/config.toml + tee -a /etc/containerd/config.toml <<'EOF' + [plugins.'io.containerd.snapshotter.v1.devmapper'] + pool_name = "containerd-pool" + root_path = "/var/lib/containerd/io.containerd.snapshotter.v1.devmapper" + base_image_size = "10GB" + fs_type = "ext2" + EOF + tee -a /etc/containerd/config.toml <<'EOF' + [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.urunc] + runtime_type = "io.containerd.urunc.v2" + container_annotations = ["com.urunc.unikernel.*"] + pod_annotations = ["com.urunc.unikernel.*"] + snapshotter = "devmapper" + EOF + systemctl restart containerd + + - name: Setup block-based mountpoint + run: | + mkdir -p /mnt/urunc-test + dd if=/dev/zero of=/tmp/urunc-test.img bs=1M count=512 + mkfs.ext4 /tmp/urunc-test.img + mount /tmp/urunc-test.img /mnt/urunc-test + + - name: Install CNI plugins + run: | + mkdir -p /opt/cni/bin + wget https://github.com/containernetworking/plugins/releases/download/v${{ inputs.cni_version }}/cni-plugins-linux-${{ matrix.arch }}-v${{ inputs.cni_version }}.tgz + tar Cxzvf /opt/cni/bin cni-plugins-linux-${{ matrix.arch }}-v${{ inputs.cni_version }}.tgz + + - name: Install nerdctl + run: | + wget https://github.com/containerd/nerdctl/releases/download/v1.7.5/nerdctl-1.7.5-linux-${{ matrix.arch }}.tar.gz + tar Cxzvf /usr/local/bin nerdctl-1.7.5-linux-${{ matrix.arch }}.tar.gz + + - name: Install crictl + run: | + wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.29.0/crictl-v1.29.0-linux-${{ matrix.arch }}.tar.gz + tar Cxzvf /usr/local/bin crictl-v1.29.0-linux-${{ matrix.arch }}.tar.gz + + - name: Install Firecracker + run: | + FC_URL="https://github.com/firecracker-microvm/firecracker/releases/download/v1.7.0/firecracker-v1.7.0-$(uname -m)" + wget $FC_URL -O firecracker + install -m 755 firecracker /usr/local/bin/firecracker + + - name: Install Cloud Hypervisor + run: | + CH_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v39.0/cloud-hypervisor" + wget $CH_URL + install -m 755 cloud-hypervisor /usr/local/bin/cloud-hypervisor + + - name: Install solo5 + run: | + # Solo5 usually needs to be built or downloaded as a binary + wget https://github.com/solo5/solo5/releases/download/v${{ inputs.solo5_version }}/solo5-v${{ inputs.solo5_version }}.tar.gz + tar xf solo5-v${{ inputs.solo5_version }}.tar.gz + cd solo5-v${{ inputs.solo5_version }} + ./configure.sh + make + make install + + - name: Download urunc artifact + uses: actions/download-artifact@v4 # v4.1.8 + with: + name: urunc-${{ matrix.arch }} + + - name: Download containerd-shim-urunc-v2 artifact + uses: actions/download-artifact@v4 # v4.1.8 + with: + name: containerd-shim-urunc-v2-${{ matrix.arch }} + + - name: Install urunc + run: | + install -m 755 urunc /usr/local/sbin/urunc + install -m 755 containerd-shim-urunc-v2 /usr/local/sbin/containerd-shim-urunc-v2 + + - name: Configure urunc with debug logging + run: | + mkdir -p /etc/urunc + echo '{"log_level": "debug"}' | tee /etc/urunc/config.json + + - name: Add runner user to KVM group + run: | + usermod -aG kvm $(whoami) + + - name: Prepare urunc folder + run: | + mkdir -p /var/lib/urunc + + - name: Run ${{ matrix.test }} + run: | + case "${{ matrix.test }}" in + test_ctr) ./tests/e2e/test_ctr.sh ;; + test_nerdctl) ./tests/e2e/test_nerdctl.sh ;; + test_crictl) ./tests/e2e/test_crictl.sh ;; + test_docker) ./tests/e2e/test_docker.sh ;; + esac + + - name: Dump urunc logs on failure + if: failure() + run: | + journalctl -u containerd --no-pager + cat /tmp/containerd.log || true + diff --git a/Makefile b/Makefile index dd694c7b0..dbf4c4984 100644 --- a/Makefile +++ b/Makefile @@ -72,8 +72,6 @@ URUNC_SRC += $(wildcard $(CURDIR)/pkg/unikontainers/types/*.go) URUNC_SRC += $(wildcard $(CURDIR)/pkg/unikontainers/initrd/*.go) URUNC_SRC += $(wildcard $(CURDIR)/pkg/network/*.go) SHIM_SRC := $(wildcard $(CURDIR)/cmd/containerd-shim-urunc-v2/*.go) -SHIM_SRC += $(wildcard $(CURDIR)/pkg/containerd-shim/*.go) -SHIM_SRC += $(wildcard $(CURDIR)/pkg/containerd-shim/containerd/*.go) #? CNTR_TOOL Tool to run the linter container (default: docker) CNTR_TOOL ?= docker diff --git a/cmd/containerd-shim-urunc-v2/main.go b/cmd/containerd-shim-urunc-v2/main.go index dac1451c2..ec7c6bc5a 100644 --- a/cmd/containerd-shim-urunc-v2/main.go +++ b/cmd/containerd-shim-urunc-v2/main.go @@ -18,8 +18,8 @@ import ( "context" "github.com/containerd/containerd/runtime/v2/runc/manager" + _ "github.com/containerd/containerd/runtime/v2/runc/task/plugin" "github.com/containerd/containerd/runtime/v2/shim" - _ "github.com/urunc-dev/urunc/pkg/containerd-shim" ) func main() { diff --git a/cmd/urunc/kill.go b/cmd/urunc/kill.go index 9d2cf4f31..7b2e29cf4 100644 --- a/cmd/urunc/kill.go +++ b/cmd/urunc/kill.go @@ -16,16 +16,11 @@ package main import ( "context" - "fmt" "os" "runtime" - "strconv" - "strings" "github.com/sirupsen/logrus" "github.com/urfave/cli/v3" - - "golang.org/x/sys/unix" ) var killCommand = &cli.Command{ @@ -41,6 +36,13 @@ For example, if the container id is "ubuntu01" the following will send a "KILL" signal to the init process of the "ubuntu01" container: # urunc kill ubuntu01 KILL`, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "all", + Aliases: []string{"a"}, + Usage: "send the specified signal to all processes inside the container", + }, + }, Action: func(_ context.Context, cmd *cli.Command) error { runtime.GOMAXPROCS(1) runtime.LockOSThread() @@ -57,34 +59,6 @@ signal to the init process of the "ubuntu01" container: if err != nil { return err } - - sigstr := cmd.Args().Get(1) - if sigstr == "" { - sigstr = "SIGTERM" - } - - signal, err := parseSignal(sigstr) - if err != nil { - return err - } - - return unikontainer.Signal(signal) + return unikontainer.Kill() }, } - -// Taken from https://github.com/opencontainers/runc/blob/af6b9e7fa96026c4fdac93e6e4fddf69ae348532/kill.go -func parseSignal(rawSignal string) (unix.Signal, error) { - s, err := strconv.Atoi(rawSignal) - if err == nil { - return unix.Signal(s), nil - } - sig := strings.ToUpper(rawSignal) - if !strings.HasPrefix(sig, "SIG") { - sig = "SIG" + sig - } - signal := unix.SignalNum(sig) - if signal == 0 { - return -1, fmt.Errorf("unknown signal %q", rawSignal) - } - return signal, nil -} diff --git a/cmd/urunc/main.go b/cmd/urunc/main.go index ab4702909..e4fc2ff06 100644 --- a/cmd/urunc/main.go +++ b/cmd/urunc/main.go @@ -113,7 +113,6 @@ func main() { deleteCommand, killCommand, runCommand, - psCommand, // specCommand, startCommand, // stateCommand, diff --git a/cmd/urunc/ps.go b/cmd/urunc/ps.go deleted file mode 100644 index 47de3c2fd..000000000 --- a/cmd/urunc/ps.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2023-2026, Nubificus LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "encoding/json" - "fmt" - "os" - - "github.com/sirupsen/logrus" - "github.com/urfave/cli/v3" -) - -var psCommand = &cli.Command{ - Name: "ps", - Usage: "displays the host-visible monitor processes associated with a container", - ArgsUsage: ``, - Description: `The ps command displays the host-visible process IDs associated -with a urunc container. This currently returns the host-visible monitor PID -stored in urunc state.json. - -This command intentionally implements the runc-compatible interface required by -containerd-shim-runc-v2/go-runc: - - urunc ps --format json - -The JSON format must be a JSON array of integers, for example: - - [12345] -`, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "format", - Aliases: []string{"f"}, - Value: "table", - Usage: "select output format: table or json", - }, - }, - Action: func(_ context.Context, cmd *cli.Command) error { - logrus.WithField("command", "PS").WithField("args", os.Args).Debug("urunc INVOKED") - - if err := checkArgs(cmd, 1, minArgs); err != nil { - return err - } - - unikontainer, err := getUnikontainer(cmd) - if err != nil { - return err - } - - // The host-visible process for the current implementation is the - // monitor process saved in state.json as State.Pid. - // - // Keep the return value as []int to match runc's ps implementation - // and containerd/go-runc's expectation for `ps --format json`. - pids := []int{unikontainer.State.Pid} - - switch cmd.String("format") { - case "json": - return json.NewEncoder(os.Stdout).Encode(pids) - - case "table": - fmt.Fprintln(os.Stdout, "PID") - for _, pid := range pids { - fmt.Fprintln(os.Stdout, pid) - } - return nil - - default: - return fmt.Errorf("invalid format option: %s", cmd.String("format")) - } - }, -} diff --git a/docs/assets/stylesheets/theme.css b/docs/assets/stylesheets/theme.css index 92181268c..4ba0a7c82 100644 --- a/docs/assets/stylesheets/theme.css +++ b/docs/assets/stylesheets/theme.css @@ -6,14 +6,14 @@ Light Theme: urunc ======================= */ [data-md-color-scheme="urunc"] { - --md-primary-fg-color: #fafaff; + --md-primary-fg-color: #FAFAFF; --md-primary-fg-color--light: #001524; --md-primary-fg-color--dark: #001524; --md-primary-bg-color: #001524; --md-primary-bg-color--light: #001524; --md-primary-bg-color--dark: #001524; - --md-accent-fg-color: #ff7d00; + --md-accent-fg-color: #FF7D00; --md-accent-fg-color--transparent: hsla(32, 100%, 55%, 0.1); --md-footer-logo-dark-mode: none; @@ -29,7 +29,7 @@ --md-primary-fg-color--dark: #001524; --md-primary-bg-color: #fff3f0; - --md-accent-fg-color: #ff7d00; + --md-accent-fg-color: #FF7D00; --md-accent-fg-color--transparent: hsla(32, 100%, 55%, 0.1); --md-footer-logo-dark-mode: block; @@ -145,7 +145,7 @@ .md-typeset code, .md-typeset pre { - font-family: "JetBrains Mono", "Fira Code", monospace; + font-family: 'JetBrains Mono', 'Fira Code', monospace; } /* Fix active link color in dark mode sidebar */ @@ -179,19 +179,3 @@ display: none; /* Hide light images in dark mode */ } -.md-clipboard { - opacity: 1 !important; -} - -.md-clipboard:hover { - background-color: var(--md-accent-fg-color--transparent); -} - -[data-md-color-scheme="slate"] .md-clipboard:not(:hover) { - color: var(--md-default-fg-color--light); -} - -/* Hide copy button for console blocks (commands with output) */ -.language-console .md-code__nav { - display: none; -} diff --git a/docs/developer-guide/debugging.md b/docs/developer-guide/debugging.md index d951e320e..ed8055f53 100644 --- a/docs/developer-guide/debugging.md +++ b/docs/developer-guide/debugging.md @@ -42,7 +42,7 @@ cargo install cntr 2. **Get the container ID:** - ```console + ```bash $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 56b93fbd7332 harbor.nbfc.io/nubificus/urunc/dbg/ubuntu:dltme "/bin/bash" 3 seconds ago Up 3 seconds urunc-debug @@ -58,7 +58,7 @@ You now have an interactive shell with access to debugging tools! ### Output: -```console +```bash $ sudo cntr attach 56b93fbd7332 root@host:/var/lib/cntr# diff --git a/docs/explorations/e2e-multi-distro-options.md b/docs/explorations/e2e-multi-distro-options.md new file mode 100644 index 000000000..797d7c79d --- /dev/null +++ b/docs/explorations/e2e-multi-distro-options.md @@ -0,0 +1,50 @@ +# Exploration: Multi-Distribution E2E Testing for urunc + +## Objective +As part of Issue #208, we explored options to expand the E2E test suite to distributions beyond Ubuntu (the default GitHub Actions runner). + +## Options Explored + +### 1. GitHub Actions Job Containers +**Description**: Use the `container:` property in GitHub Actions jobs to run steps inside a specific Docker image (e.g., Fedora, Rocky Linux, OpenSUSE). +- **Pros**: + - Natively supported by GitHub Actions. + - Easy to matrix over different images. + - Fast setup compared to full VMs. +- **Cons**: + - No native `systemd` support in standard containers (requires workarounds for `containerd`). + - Requires passing `/dev/kvm` and other host devices to the container. + - Path differences and package manager variations must be handled in scripts. + +### 2. Vagrant with KVM/QEMU +**Description**: Use Vagrant to provision full virtual machines on the GHA runner. +- **Pros**: + - Full OS isolation with `systemd` and kernel modules. + - Closest to real-world production environments. +- **Cons**: + - Requires nested virtualization support on the runner. + - Significant overhead and slower execution. + - Complex setup in GHA (managing providers like libvirt/virtualbox). + +### 3. Docker-in-Docker (Kind-like approach) +**Description**: Run tests inside a container that is started via `docker run` from the host, similar to how `kind_test.yml` works. +- **Pros**: + - Allows fine-grained control over container options (privileged, volume mounts). +- **Cons**: + - Adds an extra layer of abstraction. + - Scripting becomes more complex (`docker exec` everywhere). + +## Proposed Implementation (PoC) + +We chose to implement a **hybrid approach using Job Containers** for simplicity and integration with existing GHA workflows, while adding distro-agnostic logic to the setup scripts. + +### Implementation Details +1. **Workflow Matrix**: Updated `.github/workflows/vm_test.yml` to include a `distro` matrix. +2. **Container Configuration**: Jobs for non-Ubuntu distros use specified container images. +3. **Distro-Agnostic Setup**: + - Replaced hardcoded `apt-get` with a logic that detects the package manager (`apt`, `dnf`, `zypper`). + - Added conditional logic for `systemd` vs. direct process execution for `containerd`. + - Ensured dependencies like `libseccomp` and `qemu` are correctly mapped to distro-specific package names. + +## Conclusion +The Job Container approach provides a good balance between test coverage and complexity. While it requires bypassing `systemd` in some cases, it effectively validates `urunc`'s compatibility with different system libraries and package versions across various Linux distributions. diff --git a/docs/installation.md b/docs/installation.md index fdfcea274..96dcae609 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -216,7 +216,7 @@ sudo systemctl restart containerd Verify that the devmapper snapshotter is properly configured with: -```console +```bash $ sudo ctr plugin ls | grep devmapper io.containerd.snapshotter.v1 devmapper linux/amd64 ok ``` @@ -275,7 +275,7 @@ sudo systemctl restart containerd Verify that the blockfile snapshotter is properly configured with: -```console +```bash $ sudo ctr plugin ls | grep blockfile io.containerd.snapshotter.v1 blockfile linux/amd64 ok ``` diff --git a/docs/tutorials/Running-urunc-with-kind.md b/docs/tutorials/Running-urunc-with-kind.md index 79ba80ea4..de0440ff7 100644 --- a/docs/tutorials/Running-urunc-with-kind.md +++ b/docs/tutorials/Running-urunc-with-kind.md @@ -276,7 +276,7 @@ Define the `urunc` RuntimeClass for Kubernetes. You should see Unikraft logs indicating NGINX startup without errors. Output: - ```console + ``` [ 1.118355] Info: [libukcpio] Extracting /./nginx/logs/error.log (0 bytes) [ 1.150129] Info: [libukcpio] Creating directory /./nginx/html [ 1.178385] Info: [libukcpio] Extracting /./nginx/html/index.html (180 bytes) @@ -305,6 +305,6 @@ Confirm that the `nginx-urunc` Pod uses the `urunc` runtime. kubectl describe pod ``` Look for: - ```console + ``` Runtime Class Name: urunc ``` diff --git a/docs/tutorials/Running-vaccel-with-urunc.md b/docs/tutorials/Running-vaccel-with-urunc.md index a7fc6afc1..30dff8ab1 100644 --- a/docs/tutorials/Running-vaccel-with-urunc.md +++ b/docs/tutorials/Running-vaccel-with-urunc.md @@ -28,7 +28,7 @@ classify /usr/share/vaccel/images/example.jpg 1 ``` ### Expected output -```console +```bash 2025.12.11-15:02:16.45 - vAccel 0.7.1-51-499fc2f7 2025.12.11-15:02:16.50 - Registered plugin rpc 0.2.1-10-3d4d748c Initialized session with id: 1 @@ -41,7 +41,7 @@ export VACCEL_LOG_LEVEL=4 classify /usr/share/vaccel/images/example.jpg 1 ``` The expected output is: -```console +```bash 2025.12.11-15:03:04.72 - Initializing vAccel 2025.12.11-15:03:04.72 - vAccel 0.7.1-51-499fc2f7 2025.12.11-15:03:04.72 - Config: diff --git a/docs/tutorials/eks-tutorial.md b/docs/tutorials/eks-tutorial.md index 0bf5f1809..103de884d 100644 --- a/docs/tutorials/eks-tutorial.md +++ b/docs/tutorials/eks-tutorial.md @@ -195,7 +195,7 @@ bash get_pub_subnets.sh ``` Example output: -```console +``` subnet-02bcaca5ac39eca7a,subnet-0d0667e2156169998 ``` #### Create the EKS Cluster with Calico CNI @@ -219,7 +219,7 @@ eksctl create cluster \ ``` Example output: -```console +``` 2 sequential tasks: { create cluster control plane "urunc-tutorial", wait for control plane to become ready } 2025-04-02 12:29:16 [ℹ] building cluster stack "eksctl-urunc-tutorial-cluster" @@ -245,7 +245,7 @@ kubectl delete daemonset -n kube-system aws-node ``` Expected output: -```console +``` daemonset.apps "aws-node" deleted ``` @@ -259,7 +259,7 @@ kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v[[ ver > the above command. If it does, try to re-issue the command. Expected output: -```console +``` namespace/tigera-operator created customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created customresourcedefinition.apiextensions.k8s.io/bgpfilters.crd.projectcalico.org created @@ -307,7 +307,7 @@ EOF Expected output: -```console +``` installation.operator.tigera.io/default created ``` @@ -365,7 +365,7 @@ EOF ``` Example output: -```console +``` 2025-04-02 12:39:44 [ℹ] will use version 1.30 for new nodegroup(s) based on control plane version 2025-04-02 12:39:46 [!] "aws-node" was not found 2025-04-02 12:39:48 [ℹ] nodegroup "a1-metal-cni" will use "ami-0eb5f4a5031f47d7b" [Ubuntu2204/1.30] @@ -454,7 +454,7 @@ done We have successfully setup the cluster. Let's see what we have using a simple `kubectl get pods -o wide -A`: -```console +``` NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES calico-system calico-kube-controllers-64cf794c44-jnggx 1/1 Running 0 3m52s 172.16.50.196 ip-192-168-32-137.eu-central-1.compute.internal calico-system calico-node-xcqrj 1/1 Running 0 3m47s 192.168.32.137 ip-192-168-32-137.eu-central-1.compute.internal @@ -519,7 +519,7 @@ kubectl get pods -o wide ``` Example output: -```console +``` NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-stock-7d54d66484-k9rj5 1/1 Running 0 42s 172.16.50.197 ip-192-168-32-137.eu-central-1.compute.internal nginx-stock-7d54d66484-nn696 1/1 Running 0 42s 172.16.139.2 ip-192-168-103-211.eu-central-1.compute.internal @@ -532,13 +532,13 @@ kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash ``` Expected output: -```console +``` If you don't see a command prompt, try pressing enter. tmp-shell:~# ``` If we issue a simple `curl` command to one of the pods IPs, we should get a response from the NGINX server: -```console +``` tmp-shell:~# curl 172.16.139.2 @@ -609,7 +609,7 @@ kubectl logs -f -n kube-system -l name=urunc-deploy ``` Example output: -```console +``` Installing qemu Installing solo5-hvt Installing solo5-spt @@ -688,14 +688,14 @@ Issuing the command below: kubectl apply -f nginx-urunc.yaml ``` will produce the following output: -```console +``` deployment.apps/nginx-urunc created service/nginx-urunc created ``` and will create a deployment of an NGINX unikernel, from the container image pushed at `harbor.nbfc.io/nubificus/urunc/nginx-hvt-rumprun-block:latest` Inspecting the pods with `kubectl get pods -o wide` reveals the status: -```console +``` default nginx-urunc-998b889c4-x798f 1/1 Running 0 2s 172.16.50.225 ip-192-168-32-137.eu-central-1.compute.internal ``` @@ -705,14 +705,14 @@ and following up on the previous test, we do: kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash ``` To get a shell in a pod in the cluster: -```console +``` If you don't see a command prompt, try pressing enter. tmp-shell:~# ``` and we `curl` the pod's IP: -```console +``` tmp-shell:~# curl 172.16.50.225 diff --git a/go.mod b/go.mod index 822528008..2da9e6ad0 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,6 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/cavaliergopher/cpio v1.0.1 github.com/containerd/containerd v1.7.30 - github.com/containerd/containerd/api v1.10.0 - github.com/containerd/ttrpc v1.2.7 github.com/creack/pty v1.1.24 github.com/elastic/go-seccomp-bpf v1.6.0 github.com/hashicorp/go-version v1.9.0 @@ -28,7 +26,6 @@ require ( github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netns v0.0.5 golang.org/x/sys v0.43.0 - google.golang.org/grpc v1.79.3 k8s.io/cri-api v0.35.4 ) @@ -39,6 +36,7 @@ require ( github.com/cilium/ebpf v0.20.0 // indirect github.com/containerd/cgroups/v3 v3.1.0 // indirect github.com/containerd/console v1.0.5 // indirect + github.com/containerd/containerd/api v1.10.0 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -46,6 +44,7 @@ require ( github.com/containerd/go-runc v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -80,6 +79,7 @@ require ( golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/mkdocs.yml b/mkdocs.yml index f360ef1d7..734d6a13d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,9 +72,11 @@ theme: icon: material/brightness-4 name: Switch to light mode - features: - - navigation.instant - - content.code.copy +features: + - navigation.instant + - navigation.sections + - navigation.expand + - toc.integrate markdown_extensions: - footnotes diff --git a/pkg/containerd-shim/containerd/session.go b/pkg/containerd-shim/containerd/session.go deleted file mode 100644 index 996c0be83..000000000 --- a/pkg/containerd-shim/containerd/session.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2023-2026, Nubificus LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package containerd - -import ( - "context" - "fmt" - "time" - - containersapi "github.com/containerd/containerd/api/services/containers/v1" - contentapi "github.com/containerd/containerd/api/services/content/v1" - imagesapi "github.com/containerd/containerd/api/services/images/v1" - leasesapi "github.com/containerd/containerd/api/services/leases/v1" - snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1" - "github.com/containerd/containerd/defaults" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/namespaces" - "github.com/containerd/containerd/pkg/dialer" - "google.golang.org/grpc" - "google.golang.org/grpc/backoff" - "google.golang.org/grpc/credentials/insecure" -) - -const defaultConnectTimeout = 10 * time.Second - -type Session struct { - conn *grpc.ClientConn - - namespace string - containerID string - container *containersapi.Container -} - -// OpenSession opens a containerd session and loads the task container metadata. -func OpenSession(ctx context.Context, address, containerID string) (*Session, error) { - if address == "" { - return nil, fmt.Errorf("containerd address is empty") - } - if containerID == "" { - return nil, fmt.Errorf("container id is empty") - } - - namespace, err := namespaces.NamespaceRequired(ctx) - if err != nil { - return nil, err - } - - backoffConfig := backoff.DefaultConfig - backoffConfig.MaxDelay = 3 * time.Second - dialOptions := []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.FailOnNonTempDialError(true), - grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoffConfig}), - grpc.WithContextDialer(dialer.ContextDialer), - grpc.WithReturnConnectionError(), - grpc.WithDefaultCallOptions( - grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize), - grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize), - ), - } - - dialCtx, cancel := context.WithTimeout(ctx, defaultConnectTimeout) - defer cancel() - - conn, err := grpc.DialContext(dialCtx, dialer.DialAddress(address), dialOptions...) - if err != nil { - return nil, fmt.Errorf("dial containerd at %q: %w", address, err) - } - - session := &Session{ - conn: conn, - namespace: namespace, - containerID: containerID, - } - - container, err := loadContainer(ctx, namespace, containerID, session.containersClient()) - if err != nil { - if closeErr := conn.Close(); closeErr != nil { - return nil, fmt.Errorf("loadContainer failed: %w; close containerd connection: %v", err, closeErr) - } - return nil, fmt.Errorf("loadContainer failed: %w", err) - } - session.container = container - - return session, nil -} - -func (s *Session) Close() error { - if s == nil || s.conn == nil { - return nil - } - return s.conn.Close() -} - -func (s *Session) GetNamespace() string { - return s.namespace -} - -func (s *Session) GetContainerID() string { - return s.containerID -} - -func (s *Session) GetContainer() *containersapi.Container { - return s.container -} - -func loadContainer(ctx context.Context, namespace, containerID string, client containersapi.ContainersClient) (*containersapi.Container, error) { - resp, err := client.Get(withNamespace(ctx, namespace), &containersapi.GetContainerRequest{ - ID: containerID, - }) - if err != nil { - return nil, fmt.Errorf("get container %q: %w", containerID, containerdErr(err)) - } - container := resp.GetContainer() - if container == nil { - return nil, fmt.Errorf("get container %q: response missing container", containerID) - } - - return container, nil -} - -func withNamespace(ctx context.Context, namespace string) context.Context { - if ctx == nil { - ctx = context.Background() - } - return namespaces.WithNamespace(ctx, namespace) -} - -func containerdErr(err error) error { - if err == nil { - return nil - } - return errdefs.FromGRPC(err) -} - -func (s *Session) containersClient() containersapi.ContainersClient { - return containersapi.NewContainersClient(s.conn) -} - -//nolint:unused // Used by follow-up feature-specific access constructors. -func (s *Session) imagesClient() imagesapi.ImagesClient { - return imagesapi.NewImagesClient(s.conn) -} - -//nolint:unused // Used by follow-up feature-specific access constructors. -func (s *Session) contentClient() contentapi.ContentClient { - return contentapi.NewContentClient(s.conn) -} - -//nolint:unused // Used by follow-up feature-specific access constructors. -func (s *Session) snapshotsClient() snapshotsapi.SnapshotsClient { - return snapshotsapi.NewSnapshotsClient(s.conn) -} - -//nolint:unused // Used by follow-up feature-specific access constructors. -func (s *Session) leasesClient() leasesapi.LeasesClient { - return leasesapi.NewLeasesClient(s.conn) -} diff --git a/pkg/containerd-shim/task_plugin.go b/pkg/containerd-shim/task_plugin.go deleted file mode 100644 index 85226f383..000000000 --- a/pkg/containerd-shim/task_plugin.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2023-2026, Nubificus LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package containerdshim - -import ( - "github.com/containerd/containerd/pkg/shutdown" - "github.com/containerd/containerd/plugin" - runcTask "github.com/containerd/containerd/runtime/v2/runc/task" - "github.com/containerd/containerd/runtime/v2/shim" -) - -func init() { - plugin.Register(&plugin.Registration{ - Type: plugin.TTRPCPlugin, - ID: "task", - Requires: []plugin.Type{ - plugin.EventPlugin, - plugin.InternalPlugin, - }, - InitFn: func(ic *plugin.InitContext) (interface{}, error) { - pp, err := ic.GetByID(plugin.EventPlugin, "publisher") - if err != nil { - return nil, err - } - - ss, err := ic.GetByID(plugin.InternalPlugin, "shutdown") - if err != nil { - return nil, err - } - - inner, err := runcTask.NewTaskService(ic.Context, pp.(shim.Publisher), ss.(shutdown.Service)) - if err != nil { - return nil, err - } - - return &taskService{ - TaskService: inner, - containerdAddress: ic.Address, - }, nil - }, - }) -} diff --git a/pkg/containerd-shim/task_service.go b/pkg/containerd-shim/task_service.go deleted file mode 100644 index 70fb44a78..000000000 --- a/pkg/containerd-shim/task_service.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2023-2026, Nubificus LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package containerdshim - -import ( - "context" - - taskAPI "github.com/containerd/containerd/api/runtime/task/v2" - "github.com/containerd/ttrpc" -) - -// taskService is urunc's shim-side wrapper around containerd's runc task -// service. It currently forwards calls to the wrapped service while keeping a -// urunc-owned place for task-level feature wiring. -type taskService struct { - taskAPI.TaskService - - containerdAddress string -} - -func (s *taskService) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*taskAPI.CreateTaskResponse, error) { - return s.TaskService.Create(ctx, r) -} - -func (s *taskService) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) { - return s.TaskService.Delete(ctx, r) -} - -func (s *taskService) RegisterTTRPC(server *ttrpc.Server) error { - taskAPI.RegisterTaskService(server, s) - return nil -} diff --git a/pkg/unikontainers/hypervisors/cloud_hypervisor.go b/pkg/unikontainers/hypervisors/cloud_hypervisor.go index 606a3c02e..9794153c8 100644 --- a/pkg/unikontainers/hypervisors/cloud_hypervisor.go +++ b/pkg/unikontainers/hypervisors/cloud_hypervisor.go @@ -19,7 +19,6 @@ import ( "strings" "github.com/urunc-dev/urunc/pkg/unikontainers/types" - "golang.org/x/sys/unix" ) const ( @@ -32,10 +31,6 @@ type CloudHypervisor struct { binary string } -func (ch *CloudHypervisor) Signal(pid int, signal unix.Signal) error { - return unix.Kill(pid, signal) -} - func (ch *CloudHypervisor) Stop(pid int) error { return killProcess(pid) } diff --git a/pkg/unikontainers/hypervisors/firecracker.go b/pkg/unikontainers/hypervisors/firecracker.go index 9588a45bf..fc739136a 100644 --- a/pkg/unikontainers/hypervisors/firecracker.go +++ b/pkg/unikontainers/hypervisors/firecracker.go @@ -22,7 +22,6 @@ import ( "strings" "github.com/urunc-dev/urunc/pkg/unikontainers/types" - "golang.org/x/sys/unix" ) const ( @@ -76,10 +75,6 @@ type FirecrackerConfig struct { VSock FirecrackerVSockDev `json:"vsock,omitempty"` } -func (fc *Firecracker) Signal(pid int, signal unix.Signal) error { - return unix.Kill(pid, signal) -} - func (fc *Firecracker) Stop(pid int) error { return killProcess(pid) } diff --git a/pkg/unikontainers/hypervisors/hedge.go b/pkg/unikontainers/hypervisors/hedge.go index 7051ba7f0..329f82dbf 100644 --- a/pkg/unikontainers/hypervisors/hedge.go +++ b/pkg/unikontainers/hypervisors/hedge.go @@ -19,7 +19,6 @@ import ( hedge "github.com/nubificus/hedge_cli/hedge_api" "github.com/urunc-dev/urunc/pkg/unikontainers/types" - "golang.org/x/sys/unix" ) const ( @@ -34,10 +33,6 @@ func (h *Hedge) Ok() error { return fmt.Errorf("hedge not implemented yet") } -func (h *Hedge) Signal(_ int, _ unix.Signal) error { - return fmt.Errorf("hedge not implemented yet") -} - func (h *Hedge) Stop(_ int) error { return fmt.Errorf("hedge not implemented yet") } diff --git a/pkg/unikontainers/hypervisors/hvt.go b/pkg/unikontainers/hypervisors/hvt.go index 69cb12b91..5b25d425d 100644 --- a/pkg/unikontainers/hypervisors/hvt.go +++ b/pkg/unikontainers/hypervisors/hvt.go @@ -21,7 +21,6 @@ import ( seccomp "github.com/elastic/go-seccomp-bpf" "github.com/urunc-dev/urunc/pkg/unikontainers/types" - "golang.org/x/sys/unix" ) const ( @@ -120,10 +119,6 @@ func applySeccompFilter() error { return nil } -func (h *HVT) Signal(pid int, signal unix.Signal) error { - return unix.Kill(pid, signal) -} - // Stop kills the hvt process func (h *HVT) Stop(pid int) error { return killProcess(pid) diff --git a/pkg/unikontainers/hypervisors/qemu.go b/pkg/unikontainers/hypervisors/qemu.go index 1ac77f870..9cd7ce0eb 100644 --- a/pkg/unikontainers/hypervisors/qemu.go +++ b/pkg/unikontainers/hypervisors/qemu.go @@ -20,7 +20,6 @@ import ( "strings" "github.com/urunc-dev/urunc/pkg/unikontainers/types" - "golang.org/x/sys/unix" ) const ( @@ -34,10 +33,6 @@ type Qemu struct { vhost bool } -func (q *Qemu) Signal(pid int, signal unix.Signal) error { - return unix.Kill(pid, signal) -} - func (q *Qemu) Stop(pid int) error { return killProcess(pid) } diff --git a/pkg/unikontainers/hypervisors/spt.go b/pkg/unikontainers/hypervisors/spt.go index 60b5401f6..26b7cde1e 100644 --- a/pkg/unikontainers/hypervisors/spt.go +++ b/pkg/unikontainers/hypervisors/spt.go @@ -19,7 +19,6 @@ import ( "strings" "github.com/urunc-dev/urunc/pkg/unikontainers/types" - "golang.org/x/sys/unix" ) const ( @@ -32,10 +31,6 @@ type SPT struct { binary string } -func (s *SPT) Signal(pid int, signal unix.Signal) error { - return unix.Kill(pid, signal) -} - // Stop kills the spt process func (s *SPT) Stop(pid int) error { return killProcess(pid) diff --git a/pkg/unikontainers/hypervisors/utils.go b/pkg/unikontainers/hypervisors/utils.go index 1bee33104..c3de7c8b8 100644 --- a/pkg/unikontainers/hypervisors/utils.go +++ b/pkg/unikontainers/hypervisors/utils.go @@ -19,6 +19,7 @@ import ( "fmt" "runtime" "strconv" + "syscall" "time" "golang.org/x/sys/unix" @@ -68,9 +69,9 @@ func BytesToStringMB(argMem uint64) string { func killProcess(pid int) error { const timeout = 2 * time.Second - err := unix.Kill(pid, unix.SIGKILL) + err := syscall.Kill(pid, unix.SIGKILL) if err != nil { - if errors.Is(err, unix.ESRCH) { + if errors.Is(err, syscall.ESRCH) { // Process already dead, nothing to do return nil } @@ -78,8 +79,8 @@ func killProcess(pid int) error { } deadline := time.Now().Add(timeout) for { - if err := unix.Kill(pid, 0); err != nil { - if errors.Is(err, unix.ESRCH) { + if err := syscall.Kill(pid, 0); err != nil { + if errors.Is(err, syscall.ESRCH) { // process is dead break } diff --git a/pkg/unikontainers/types/types.go b/pkg/unikontainers/types/types.go index c6388e2cc..9daa8c0fa 100644 --- a/pkg/unikontainers/types/types.go +++ b/pkg/unikontainers/types/types.go @@ -15,8 +15,6 @@ //revive:disable:var-naming package types -import "golang.org/x/sys/unix" - type Unikernel interface { Init(UnikernelParams) error CommandString() (string, error) @@ -37,7 +35,6 @@ type VMM interface { // filters here. Most monitors can return nil (no-op). PreExec(args ExecArgs) error Stop(int) error - Signal(int, unix.Signal) error Path() string UsesKVM() bool SupportsSharedfs(string) bool diff --git a/pkg/unikontainers/unikontainers.go b/pkg/unikontainers/unikontainers.go index b7d61def7..99af0998c 100644 --- a/pkg/unikontainers/unikontainers.go +++ b/pkg/unikontainers/unikontainers.go @@ -70,10 +70,6 @@ func New(bundlePath string, containerID string, rootDir string, cfg *UruncConfig return nil, err } - if spec == nil || spec.Linux == nil { - return nil, fmt.Errorf("invalid OCI spec: linux section is required") - } - containerName := spec.Annotations["io.kubernetes.cri.container-name"] if containerName == "queue-proxy" { uniklog.Warn("This is a queue-proxy container. Adding IP env.") @@ -129,9 +125,6 @@ func Get(containerID string, rootDir string) (*Unikontainer, error) { if err != nil { return nil, err } - if spec == nil || spec.Linux == nil { - return nil, fmt.Errorf("invalid OCI spec: linux section is required") - } u.BaseDir = containerDir u.RootDir = rootDir u.Spec = spec @@ -364,7 +357,7 @@ func (u *Unikontainer) Exec(metrics m.Writer) error { // ExecArgs // If memory limit is set in spec, use it instead of the config default value - if u.Spec.Linux.Resources != nil && u.Spec.Linux.Resources.Memory != nil { + if u.Spec.Linux.Resources.Memory != nil { if u.Spec.Linux.Resources.Memory.Limit != nil { if *u.Spec.Linux.Resources.Memory.Limit > 0 { vmmArgs.MemSizeB = uint64(*u.Spec.Linux.Resources.Memory.Limit) // nolint:gosec @@ -663,17 +656,6 @@ func setupUser(user specs.User) error { return nil } -// Signal sends a specified signal to container's init. -func (u *Unikontainer) Signal(signal unix.Signal) error { - vmmType := u.State.Annotations[annotHypervisor] - vmm, err := hypervisors.NewVMM(hypervisors.VmmType(vmmType), u.UruncCfg.Monitors) - if err != nil { - return err - } - - return vmm.Signal(u.State.Pid, signal) -} - // Kill stops the VMM process, first by asking the VMM struct to stop // and consequently by killing the process described in u.State.Pid func (u *Unikontainer) Kill() error {