diff --git a/.agent/CODING_TASTE.md b/.agent/CODING_TASTE.md index fefaf9e83..65c93260e 100644 --- a/.agent/CODING_TASTE.md +++ b/.agent/CODING_TASTE.md @@ -99,7 +99,7 @@ Argue from dstack's threat model, not from generic best practices. ## Code organization -- **Workspace-managed dependencies.** Declare versions in the root `Cargo.toml`, reference +- **Workspace-managed dependencies.** Declare versions in `dstack/Cargo.toml`, reference with `foo.workspace = true` (#161, #360). - **One source of truth.** Shared logic used by two components lives in one crate (`dstack-mr::sev` used by both KMS and verifier). Duplicated blocks get extracted — @@ -208,7 +208,7 @@ not just the symptom: an `X-App-Version` response header. Follow this skeleton exactly when adding a service. - **Module layout**: the core RPC surface lives in `main_service.rs` (or `rpc_service.rs` in guest-agent). When a module grows subordinate concerns, promote it to `foo.rs` + a - `foo/` directory of submodules (`gateway/src/proxy.rs` + `proxy/{sni,tls_terminate,...}`). + `foo/` directory of submodules (`dstack/gateway/src/proxy.rs` + `proxy/{sni,tls_terminate,...}`). Core files routinely run 800–1500+ lines before splitting — don't over-fragment into many small files. - **Config conventions**: each service embeds its default TOML (`include_str!`), extracts @@ -298,7 +298,7 @@ not just the symptom: when async is unavoidable. - **Golden vectors over mocks**: real captured binary fixtures embedded with `include_bytes!("../samples/...")`, asserted against inline hex literals or `insta` - snapshots (`cc-eventlog`, `dstack-attest/tests/`). Fixture provenance gets its own + snapshots (`dstack/cc-eventlog`, `dstack/dstack-attest/tests/`). Fixture provenance gets its own README (`sev_snp_fixture.README.md`). When changing an encoding, add a regression test proving old and new outputs match (#603). - **Test names are snake_case behavior statements**: `enforces_ttl`, diff --git a/.claude/agents/sdk-sync-checker.md b/.claude/agents/sdk-sync-checker.md index 41dc8fcb0..794123da8 100644 --- a/.claude/agents/sdk-sync-checker.md +++ b/.claude/agents/sdk-sync-checker.md @@ -11,7 +11,7 @@ You validate SDK implementations against protobuf schemas to ensure synchronizat ## Process ### 1. Discovery -- Find all `.proto` files in `guest-agent/rpc/proto/` +- Find all `.proto` files in `dstack/guest-agent/rpc/proto/` - Identify SDK implementations in `sdk/` (python, go, rust, js, curl docs) - Extract services, RPCs, and message types from proto files @@ -66,7 +66,7 @@ Details: - cURL docs: `snake_case` (JSON wire format) ## Locations -- Protos: `guest-agent/rpc/proto/*.proto` +- Protos: `dstack/guest-agent/rpc/proto/*.proto` - Python: `sdk/python/src/dstack_sdk/dstack_client.py` - Go: `sdk/go/dstack/client.go` - Rust: `sdk/rust/types/src/dstack.rs` diff --git a/.cursorrules b/.cursorrules index be2db8368..b03e196fc 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1 +1 @@ -Don't capitalize the first letter for log messages and error messages. \ No newline at end of file +Don't capitalize the first letter for log messages and error messages. diff --git a/.github/scripts/cargo-publish-idempotent.sh b/.github/scripts/cargo-publish-idempotent.sh index 5521b0148..fcff7bb38 100755 --- a/.github/scripts/cargo-publish-idempotent.sh +++ b/.github/scripts/cargo-publish-idempotent.sh @@ -11,7 +11,7 @@ set -euo pipefail crate=${1:?missing crate name} -if output=$(cargo publish -p "$crate" 2>&1); then +if output=$(cargo publish --manifest-path dstack/Cargo.toml -p "$crate" 2>&1); then echo "$output" exit 0 fi diff --git a/.github/workflows/docker-build-check.yml b/.github/workflows/docker-build-check.yml index 9ab36623e..25dcfc677 100644 --- a/.github/workflows/docker-build-check.yml +++ b/.github/workflows/docker-build-check.yml @@ -26,21 +26,21 @@ jobs: - name: Build Gateway Docker image uses: docker/build-push-action@v5 with: - context: gateway/dstack-app/builder + context: dstack/gateway/dstack-app/builder push: false load: true tags: dstack-gateway-check:latest provenance: false build-contexts: | - build-shared=build/shared + build-shared=dstack/build/shared build-args: | DSTACK_REV=${{ env.DSTACK_REV }} DSTACK_SRC_URL=${{ env.DSTACK_SRC_URL }} - name: Verify pinned packages run: | - build/shared/verify-pinned-packages.sh dstack-gateway-check:latest \ - gateway/dstack-app/builder/shared/pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh dstack-gateway-check:latest \ + dstack/gateway/dstack-app/builder/shared/pinned-packages.txt - name: Build gateway-builder target run: | @@ -49,22 +49,27 @@ jobs: --target gateway-builder \ --tag gateway-builder-check:latest \ --provenance=false \ - --build-context build-shared=build/shared \ + --build-context build-shared=dstack/build/shared \ --build-arg "DSTACK_REV=${DSTACK_REV}" \ --build-arg "DSTACK_SRC_URL=${DSTACK_SRC_URL}" \ - gateway/dstack-app/builder + dstack/gateway/dstack-app/builder - name: Verify builder pinned packages run: | - build/shared/verify-pinned-packages.sh gateway-builder-check:latest \ - gateway/dstack-app/builder/shared/builder-pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh gateway-builder-check:latest \ + dstack/gateway/dstack-app/builder/shared/builder-pinned-packages.txt kms: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - with: - submodules: recursive + + - name: Checkout KMS contract dependencies + run: | + git submodule update --init --recursive --depth 1 -- \ + dstack/kms/auth-eth/lib/forge-std \ + dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable \ + dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -72,21 +77,21 @@ jobs: - name: Build KMS Docker image uses: docker/build-push-action@v5 with: - context: kms/dstack-app/builder + context: dstack/kms/dstack-app/builder push: false load: true tags: dstack-kms-check:latest provenance: false build-contexts: | - build-shared=build/shared + build-shared=dstack/build/shared build-args: | DSTACK_REV=${{ env.DSTACK_REV }} DSTACK_SRC_URL=${{ env.DSTACK_SRC_URL }} - name: Verify pinned packages (qemu stage) run: | - build/shared/verify-pinned-packages.sh dstack-kms-check:latest \ - kms/dstack-app/builder/shared/qemu-pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh dstack-kms-check:latest \ + dstack/kms/dstack-app/builder/shared/qemu-pinned-packages.txt - name: Build kms-builder target run: | @@ -95,22 +100,22 @@ jobs: --target kms-builder \ --tag kms-builder-check:latest \ --provenance=false \ - --build-context build-shared=build/shared \ + --build-context build-shared=dstack/build/shared \ --build-arg "DSTACK_REV=${DSTACK_REV}" \ --build-arg "DSTACK_SRC_URL=${DSTACK_SRC_URL}" \ - kms/dstack-app/builder + dstack/kms/dstack-app/builder - name: Verify builder pinned packages run: | - build/shared/verify-pinned-packages.sh kms-builder-check:latest \ - kms/dstack-app/builder/shared/builder-pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh kms-builder-check:latest \ + dstack/kms/dstack-app/builder/shared/builder-pinned-packages.txt - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - name: Build KMS contracts run: | - cd kms/auth-eth + cd dstack/kms/auth-eth forge build verifier: @@ -124,22 +129,22 @@ jobs: - name: Build Verifier Docker image uses: docker/build-push-action@v5 with: - context: verifier - file: verifier/builder/Dockerfile + context: dstack/verifier + file: dstack/verifier/builder/Dockerfile push: false load: true tags: dstack-verifier-check:latest provenance: false build-contexts: | - build-shared=build/shared + build-shared=dstack/build/shared build-args: | DSTACK_REV=${{ env.DSTACK_REV }} DSTACK_SRC_URL=${{ env.DSTACK_SRC_URL }} - name: Verify pinned packages (runtime) run: | - build/shared/verify-pinned-packages.sh dstack-verifier-check:latest \ - verifier/builder/shared/pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh dstack-verifier-check:latest \ + dstack/verifier/builder/shared/pinned-packages.txt - name: Build verifier-builder target run: | @@ -148,16 +153,16 @@ jobs: --target verifier-builder \ --tag verifier-builder-check:latest \ --provenance=false \ - --file verifier/builder/Dockerfile \ - --build-context build-shared=build/shared \ + --file dstack/verifier/builder/Dockerfile \ + --build-context build-shared=dstack/build/shared \ --build-arg "DSTACK_REV=${DSTACK_REV}" \ --build-arg "DSTACK_SRC_URL=${DSTACK_SRC_URL}" \ - verifier + dstack/verifier - name: Verify builder pinned packages run: | - build/shared/verify-pinned-packages.sh verifier-builder-check:latest \ - verifier/builder/shared/builder-pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh verifier-builder-check:latest \ + dstack/verifier/builder/shared/builder-pinned-packages.txt - name: Build acpi-builder target run: | @@ -166,13 +171,13 @@ jobs: --target acpi-builder \ --tag verifier-acpi-check:latest \ --provenance=false \ - --file verifier/builder/Dockerfile \ - --build-context build-shared=build/shared \ + --file dstack/verifier/builder/Dockerfile \ + --build-context build-shared=dstack/build/shared \ --build-arg "DSTACK_REV=${DSTACK_REV}" \ --build-arg "DSTACK_SRC_URL=${DSTACK_SRC_URL}" \ - verifier + dstack/verifier - name: Verify qemu pinned packages run: | - build/shared/verify-pinned-packages.sh verifier-acpi-check:latest \ - verifier/builder/shared/qemu-pinned-packages.txt + dstack/build/shared/verify-pinned-packages.sh verifier-acpi-check:latest \ + dstack/verifier/builder/shared/qemu-pinned-packages.txt diff --git a/.github/workflows/foundry-test.yml b/.github/workflows/foundry-test.yml index 762449342..282d7056b 100644 --- a/.github/workflows/foundry-test.yml +++ b/.github/workflows/foundry-test.yml @@ -7,11 +7,11 @@ name: KMS Auth-ETH Foundry Tests on: push: paths: - - 'kms/auth-eth/**' + - 'dstack/kms/auth-eth/**' - '.github/workflows/foundry-test.yml' pull_request: paths: - - 'kms/auth-eth/**' + - 'dstack/kms/auth-eth/**' - '.github/workflows/foundry-test.yml' workflow_dispatch: @@ -27,11 +27,17 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: kms/auth-eth + working-directory: dstack/kms/auth-eth steps: - uses: actions/checkout@v5 - with: - submodules: recursive + + - name: Checkout contract dependencies + working-directory: . + run: | + git submodule update --init --recursive --depth 1 -- \ + dstack/kms/auth-eth/lib/forge-std \ + dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable \ + dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 diff --git a/.github/workflows/gateway-release.yml b/.github/workflows/gateway-release.yml index 3bc886a97..07d12796f 100644 --- a/.github/workflows/gateway-release.yml +++ b/.github/workflows/gateway-release.yml @@ -49,13 +49,13 @@ jobs: env: SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} with: - context: gateway/dstack-app/builder + context: dstack/gateway/dstack-app/builder push: true tags: ${{ vars.DOCKERHUB_ORG }}/dstack-gateway:${{ env.VERSION }} platforms: linux/amd64 provenance: false build-contexts: | - build-shared=build/shared + build-shared=dstack/build/shared build-args: | DSTACK_REV=${{ env.GIT_REV }} SOURCE_DATE_EPOCH=${{ env.TIMESTAMP }} diff --git a/.github/workflows/guest-os.yml b/.github/workflows/guest-os.yml new file mode 100644 index 000000000..dc53eb24d --- /dev/null +++ b/.github/workflows/guest-os.yml @@ -0,0 +1,95 @@ +# SPDX-FileCopyrightText: © 2026 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 + +name: Build Guest Images + +on: + workflow_dispatch: + inputs: + tag: + description: 'Tag to create and release (e.g. guest-os-v0.6.0). Leave empty to build only.' + required: false + default: '' + type: string + +jobs: + build: + runs-on: yocto-builder + timeout-minutes: 480 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Build production guest OS + run: make os-image + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: guest-images + path: | + os/yocto/repro-build/dist/*.tar.gz + os/yocto/repro-build/dist/reproduce.sh + retention-days: 30 + + release: + if: inputs.tag != '' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + needs: build + runs-on: yocto-builder + timeout-minutes: 60 + environment: release + permissions: + contents: write + steps: + - name: Checkout default branch + uses: actions/checkout@v5 + with: + ref: ${{ github.event.repository.default_branch }} + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: guest-images + path: os/yocto/repro-build/dist + + - name: Validate release tag + env: + TAG: ${{ inputs.tag }} + run: | + echo "$TAG" | grep -Eq '^guest-os-v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$' + VERSION="${TAG#guest-os-v}" + python3 - "$VERSION" <<'PY' + import re + import sys + + match = re.match(r"^(\d+)\.(\d+)\.(\d+)", sys.argv[1]) + if not match or tuple(map(int, match.groups())) < (0, 6, 0): + raise SystemExit("guest OS versions below 0.6.0 belong in Dstack-TEE/meta-dstack") + PY + BARE="os/yocto/repro-build/dist/dstack-${VERSION}.tar.gz" + UKI="os/yocto/repro-build/dist/dstack-${VERSION}-uki.tar.gz" + test -f "$BARE" + test -f "$UKI" + + read -r IMAGE_VERSION IMAGE_REVISION < <( + tar -xOf "$BARE" "dstack-${VERSION}/metadata.json" | + python3 -c 'import json, sys; data=json.load(sys.stdin); print(data["version"], data["git_revision"])' + ) + test "$IMAGE_VERSION" = "$VERSION" + test "$IMAGE_REVISION" = "$(git rev-parse HEAD)" + tar -tzf "$UKI" | grep -Fx "dstack-${VERSION}/disk.raw" + + - name: Create tag and release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ inputs.tag }} + run: | + git tag "$TAG" + git push origin "$TAG" + gh release create "$TAG" \ + os/yocto/repro-build/dist/*.tar.gz \ + os/yocto/repro-build/dist/reproduce.sh \ + --title "$TAG" --generate-notes diff --git a/.github/workflows/kms-release.yml b/.github/workflows/kms-release.yml index 7d79cec9c..0b33c5f88 100644 --- a/.github/workflows/kms-release.yml +++ b/.github/workflows/kms-release.yml @@ -22,6 +22,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v5 + - name: Checkout contract dependencies + run: | + git submodule update --init --recursive --depth 1 -- \ + dstack/kms/auth-eth/lib/forge-std \ + dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable \ + dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades + - name: Parse version from tag run: | # Extract version from tag (e.g., kms-v1.2.3 -> 1.2.3) @@ -49,13 +56,13 @@ jobs: env: SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} with: - context: kms/dstack-app/builder + context: dstack/kms/dstack-app/builder push: true tags: ${{ vars.DOCKERHUB_ORG }}/dstack-kms:${{ env.VERSION }} platforms: linux/amd64 provenance: false build-contexts: | - build-shared=build/shared + build-shared=dstack/build/shared build-args: | DSTACK_REV=${{ env.GIT_REV }} DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }} @@ -73,7 +80,7 @@ jobs: - name: Compile contracts with Foundry run: | - cd kms/auth-eth + cd dstack/kms/auth-eth forge install forge build @@ -82,8 +89,8 @@ jobs: with: name: "KMS Release v${{ env.VERSION }}" files: | - kms/auth-eth/out/DstackKms.sol/DstackKms.json - kms/auth-eth/out/DstackApp.sol/DstackApp.json + dstack/kms/auth-eth/out/DstackKms.sol/DstackKms.json + dstack/kms/auth-eth/out/DstackApp.sol/DstackApp.json body: | ## Docker Image Information diff --git a/.github/workflows/rust-sdk-release.yml b/.github/workflows/rust-sdk-release.yml index 4f6a0a974..f2aec8e5c 100644 --- a/.github/workflows/rust-sdk-release.yml +++ b/.github/workflows/rust-sdk-release.yml @@ -53,8 +53,8 @@ jobs: pkg_version("sdk/rust/types/Cargo.toml")), ("sdk/rust/Cargo.toml [package.version]", pkg_version("sdk/rust/Cargo.toml")), - ("Cargo.toml [workspace.dependencies.dstack-sdk-types.version]", - ws_dep_version("Cargo.toml", "dstack-sdk-types")), + ("dstack/Cargo.toml [workspace.dependencies.dstack-sdk-types.version]", + ws_dep_version("dstack/Cargo.toml", "dstack-sdk-types")), ] fail = False diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index de89c5962..a269d4c49 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,13 +16,15 @@ env: jobs: rust-checks: runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }} + defaults: + run: + working-directory: dstack steps: - uses: actions/checkout@v5 - name: Install Rust - uses: dtolnay/rust-toolchain@1.86 + uses: dtolnay/rust-toolchain@1.92.0 with: - toolchain: 1.92.0 components: clippy, rustfmt - name: Run Clippy diff --git a/.github/workflows/sdk.yaml b/.github/workflows/sdk.yaml index 2372ba89e..b5f7ae11a 100644 --- a/.github/workflows/sdk.yaml +++ b/.github/workflows/sdk.yaml @@ -20,12 +20,11 @@ jobs: sdk-tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Rust - uses: dtolnay/rust-toolchain@1.86 + uses: dtolnay/rust-toolchain@1.92.0 with: - toolchain: 1.92.0 components: clippy, rustfmt # This additional target is needed for wasm32 compatibility check. targets: wasm32-unknown-unknown, thumbv6m-none-eabi @@ -35,9 +34,9 @@ jobs: - name: Verify WASM compilation # Ensures SDK types can be used in smart contracts - run: cargo check --target=wasm32-unknown-unknown -p dstack-sdk-types + run: cargo check --manifest-path dstack/Cargo.toml --target=wasm32-unknown-unknown -p dstack-sdk-types - name: Verify no_std compatibility run: | - cargo test -p dstack-sdk-types --test no_std_test --no-default-features - cargo check -p no_std_check --target thumbv6m-none-eabi + cargo test --manifest-path dstack/Cargo.toml -p dstack-sdk-types --test no_std_test --no-default-features + cargo check --manifest-path dstack/Cargo.toml -p no_std_check --target thumbv6m-none-eabi diff --git a/.github/workflows/simulator-release.yml b/.github/workflows/simulator-release.yml index 7ec123fdb..79c5a267d 100644 --- a/.github/workflows/simulator-release.yml +++ b/.github/workflows/simulator-release.yml @@ -52,12 +52,14 @@ jobs: - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 + with: + workspaces: dstack -> target - name: Build musl simulator binary - run: cargo build --locked --release --target "${TARGET_TRIPLE}" -p dstack-guest-agent-simulator + run: cargo build --manifest-path dstack/Cargo.toml --locked --release --target "${TARGET_TRIPLE}" -p dstack-guest-agent-simulator - name: Package release bundle - run: ./guest-agent-simulator/package-release.sh "${VERSION}" "${TARGET_TRIPLE}" + run: ./dstack/guest-agent-simulator/package-release.sh "${VERSION}" "${TARGET_TRIPLE}" - name: GitHub Release uses: softprops/action-gh-release@v2 @@ -65,9 +67,9 @@ jobs: tag_name: ${{ env.TAG }} name: "Simulator Release v${{ env.VERSION }}" files: | - guest-agent-simulator/dist/dstack-simulator-${{ env.VERSION }}-${{ env.TARGET_TRIPLE }}.tar.gz - guest-agent-simulator/dist/dstack-simulator-${{ env.VERSION }}-${{ env.TARGET_TRIPLE }}.tar.gz.sha256 - guest-agent-simulator/install-systemd.sh + dstack/guest-agent-simulator/dist/dstack-simulator-${{ env.VERSION }}-${{ env.TARGET_TRIPLE }}.tar.gz + dstack/guest-agent-simulator/dist/dstack-simulator-${{ env.VERSION }}-${{ env.TARGET_TRIPLE }}.tar.gz.sha256 + dstack/guest-agent-simulator/install-systemd.sh body: | ## Release Assets @@ -92,5 +94,5 @@ jobs: Install to systemd: ```bash - curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/${{ env.TAG }}/guest-agent-simulator/install-systemd.sh | sudo bash -s -- --version ${{ env.VERSION }} + curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/${{ env.TAG }}/dstack/guest-agent-simulator/install-systemd.sh | sudo bash -s -- --version ${{ env.VERSION }} ``` diff --git a/.github/workflows/verifier-release.yml b/.github/workflows/verifier-release.yml index a939111ff..d02d5b3c2 100644 --- a/.github/workflows/verifier-release.yml +++ b/.github/workflows/verifier-release.yml @@ -48,8 +48,8 @@ jobs: env: SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} with: - context: verifier - file: verifier/builder/Dockerfile + context: dstack/verifier + file: dstack/verifier/builder/Dockerfile push: true tags: | ${{ vars.DOCKERHUB_ORG }}/dstack-verifier:${{ env.VERSION }} @@ -57,7 +57,7 @@ jobs: platforms: linux/amd64 provenance: false build-contexts: | - build-shared=build/shared + build-shared=dstack/build/shared build-args: | DSTACK_REV=${{ env.GIT_REV }} DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}.git diff --git a/.github/workflows/vmm-ui.yml b/.github/workflows/vmm-ui.yml index fe2980414..ea571b4ce 100644 --- a/.github/workflows/vmm-ui.yml +++ b/.github/workflows/vmm-ui.yml @@ -28,14 +28,14 @@ jobs: run: | npm ci npm run build - working-directory: vmm/ui + working-directory: dstack/vmm/ui - name: Ensure vmm UI build is clean run: | if [ -n "$(git status --porcelain)" ]; then echo "VMM UI build produced uncommitted changes." - echo "Run: cd vmm/ui && npm ci && npm run build" - echo "Then commit the updated build output vmm/src/console_v1.html" + echo "Run: cd dstack/vmm/ui && npm ci && npm run build" + echo "Then commit the updated build output dstack/vmm/src/console_v1.html" git status --porcelain exit 1 fi diff --git a/.gitignore b/.gitignore index d30f69ff4..bcb4cb1aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ -/target +/dstack/target /certs /build-config.sh /build/* -!/build/shared/ +/images +/run +/rust-target **/generated/* !**/generated/mod.rs node_modules/ @@ -11,6 +13,7 @@ node_modules/ /tmp .claude/settings.local.json __pycache__ +/.ruff_cache/ .planning/ -/vmm/src/console_v1.html +/dstack/vmm/src/console_v1.html .claude/worktrees/ diff --git a/.gitmodules b/.gitmodules index 2dc8dbacd..05449f8bc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,12 +2,47 @@ # # SPDX-License-Identifier: Apache-2.0 -[submodule "kms/auth-eth/lib/forge-std"] - path = kms/auth-eth/lib/forge-std +[submodule "dstack/kms/auth-eth/lib/forge-std"] + path = dstack/kms/auth-eth/lib/forge-std url = https://github.com/foundry-rs/forge-std -[submodule "kms/auth-eth/lib/openzeppelin-contracts-upgradeable"] - path = kms/auth-eth/lib/openzeppelin-contracts-upgradeable +[submodule "dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable"] + path = dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable -[submodule "kms/auth-eth/lib/openzeppelin-foundry-upgrades"] - path = kms/auth-eth/lib/openzeppelin-foundry-upgrades +[submodule "dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades"] + path = dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades + +[submodule "os/yocto/deps/bitbake"] + path = os/yocto/deps/bitbake + url = https://git.openembedded.org/bitbake + branch = 2.18 + +[submodule "os/yocto/deps/openembedded-core"] + path = os/yocto/deps/openembedded-core + url = https://git.openembedded.org/openembedded-core + branch = wrynose + +[submodule "os/yocto/deps/meta-yocto"] + path = os/yocto/deps/meta-yocto + url = https://git.yoctoproject.org/meta-yocto + branch = wrynose + +[submodule "os/yocto/deps/meta-confidential-compute"] + path = os/yocto/deps/meta-confidential-compute + url = https://github.com/Dstack-TEE/meta-confidential-compute.git + +[submodule "os/yocto/deps/meta-virtualization"] + path = os/yocto/deps/meta-virtualization + url = https://github.com/Dstack-TEE/meta-virtualization.git + +[submodule "os/yocto/deps/meta-openembedded"] + path = os/yocto/deps/meta-openembedded + url = https://github.com/openembedded/meta-openembedded + +[submodule "os/yocto/deps/meta-rust-bin"] + path = os/yocto/deps/meta-rust-bin + url = https://github.com/Dstack-TEE/meta-rust-bin + +[submodule "os/yocto/deps/meta-security"] + path = os/yocto/deps/meta-security + url = https://github.com/Dstack-TEE/meta-security.git diff --git a/CLAUDE.md b/CLAUDE.md index 768e68263..fb39cc38c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -dstack is a developer-friendly, security-first framwwork for deploying containerized applications into Intel TDX (Trust Domain Extensions) Trusted Execution Environments (TEEs). The system provides end-to-end security through hardware-rooted attestation, automated key management, and zero-trust networking. +dstack is a developer-friendly, security-first framework for deploying containerized applications into Intel TDX (Trust Domain Extensions) Trusted Execution Environments (TEEs). The system provides end-to-end security through hardware-rooted attestation, automated key management, and zero-trust networking. + +The monorepo keeps core services and the Rust workspace in `dstack/`, public +SDKs in `sdk/`, guest-OS builders in `os/`, documentation in `docs/`, and +standalone utilities in `tools/`. ## Architecture @@ -12,13 +16,13 @@ dstack consists of several core components that interact to provide TEE-based co ### Core Components -- **`dstack-vmm`** (`vmm/`): Virtual Machine Manager that runs on bare-metal TDX hosts. Orchestrates CVM lifecycle, manages QEMU processes, allocates resources, parses docker-compose files, and provides a web UI (port 9080) for deployment. +- **`dstack-vmm`** (`dstack/vmm/`): Virtual Machine Manager that runs on bare-metal TDX hosts. Orchestrates CVM lifecycle, manages QEMU processes, allocates resources, parses docker-compose files, and provides a web UI (port 9080) for deployment. -- **`dstack-kms`** (`kms/`): Key Management System that handles cryptographic key provisioning after TDX quote verification. Derives keys deterministically per application identity and enforces authorization policies defined in smart contracts on Ethereum. +- **`dstack-kms`** (`dstack/kms/`): Key Management System that handles cryptographic key provisioning after TDX quote verification. Derives keys deterministically per application identity and enforces authorization policies defined in smart contracts on Ethereum. -- **`dstack-gateway`** (`gateway/`): Reverse proxy providing zero-trust network access. Handles TLS termination, automated ACME certificate provisioning, and traffic routing via ingress mapping rules. +- **`dstack-gateway`** (`dstack/gateway/`): Reverse proxy providing zero-trust network access. Handles TLS termination, automated ACME certificate provisioning, and traffic routing via ingress mapping rules. -- **`dstack-guest-agent`** (`guest-agent/`): Runs inside each CVM to provide runtime services including Docker Compose lifecycle management, TDX quote generation, key provisioning from KMS, and log aggregation. Exposes API via Unix socket at `/var/run/dstack.sock`. +- **`dstack-guest-agent`** (`dstack/guest-agent/`): Runs inside each CVM to provide runtime services including Docker Compose lifecycle management, TDX quote generation, key provisioning from KMS, and log aggregation. Exposes API via Unix socket at `/var/run/dstack.sock`. ### Communication Protocols @@ -29,10 +33,10 @@ dstack consists of several core components that interact to provide TEE-based co ### Additional Components -- **`certbot`** (`certbot/`): Automated ACME DNS-01 certificate management -- **`ct_monitor`** (`ct_monitor/`): Certificate Transparency log monitoring -- **`verifier`** (`verifier/`): TDX quote verification service using `dcap-qvl` -- **`supervisor`** (`supervisor/`): Process supervision inside CVMs +- **`certbot`** (`dstack/certbot/`): Automated ACME DNS-01 certificate management +- **`ct_monitor`** (`dstack/ct_monitor/`): Certificate Transparency log monitoring +- **`verifier`** (`dstack/verifier/`): TDX quote verification service using `dcap-qvl` +- **`supervisor`** (`dstack/supervisor/`): Process supervision inside CVMs - **SDKs** (`sdk/`): Client SDKs in Rust, Python, Go, and JavaScript for interacting with guest-agent APIs ## Build Commands @@ -40,6 +44,8 @@ dstack consists of several core components that interact to provide TEE-based co ### Rust Components ```bash +cd dstack + # Build all components cargo build --release @@ -63,7 +69,7 @@ cargo clippy -- -D warnings --allow unused_variables ### Ethereum Smart Contracts (KMS Auth) ```bash -cd kms/auth-eth +cd dstack/kms/auth-eth npm install # Install Node.js dependencies for bootAuth server forge install # Install Foundry dependencies (submodules) @@ -94,7 +100,7 @@ make test # Run tests ```bash # Run all Rust tests (requires simulator) -./run-tests.sh +./dstack/run-tests.sh ``` This script: @@ -106,6 +112,8 @@ This script: ### Running Specific Tests ```bash +cd dstack + # Run tests for a specific package cargo test -p dstack-kms --all-features @@ -119,7 +127,7 @@ cargo test --all-features -- --show-output --test-threads=1 ### Foundry Tests (Ethereum Contracts) ```bash -cd kms/auth-eth +cd dstack/kms/auth-eth # Run all Foundry tests forge test @@ -168,7 +176,7 @@ This rule is enforced in `.cursorrules`. ### Local Development Setup -1. Build meta-dstack artifacts (see README.md section "Build and Run") +1. Build guest-OS artifacts through `os/build.sh` (see `os/README.md`) 2. Download or build guest OS image 3. Run components in separate terminals: - KMS: `./dstack-kms -c kms.toml` @@ -193,10 +201,10 @@ Ingress mapping pattern: `[-[][s|g]].` ## Important Files -- `Cargo.toml`: Workspace configuration with all Rust crates -- `vmm.toml`: VMM configuration (CID pool, port mapping, KMS/gateway URLs) -- `kms.toml`: KMS configuration (contract addresses, RPC endpoints) -- `gateway.toml`: Gateway configuration (domain, certificates, WireGuard) +- `dstack/Cargo.toml`: Workspace configuration with all Rust crates +- `dstack/vmm/vmm.toml`: VMM configuration (CID pool, port mapping, KMS/gateway URLs) +- `dstack/kms/kms.toml`: KMS configuration (contract addresses, RPC endpoints) +- `dstack/gateway/gateway.toml`: Gateway configuration (domain, certificates, WireGuard) - `docker-compose.yaml`: App deployment format (normalized to `.app-compose.json`) ## Common Tasks @@ -204,7 +212,7 @@ Ingress mapping pattern: `[-[][s|g]].` ### Adding a New Rust Crate 1. Create crate directory and `Cargo.toml` -2. Add to workspace members in root `Cargo.toml` +2. Add to workspace members in `dstack/Cargo.toml` 3. Add workspace dependency if it will be used by other crates ### Modifying RPC APIs @@ -216,9 +224,9 @@ RPC definitions use `prpc` framework with Protocol Buffers: ### Working with TDX Quotes -- Pure Rust API: `tdx-attest/` -- Verification: `verifier/` using `dcap-qvl` -- Event log parsing: `cc-eventlog/` +- Pure Rust API: `dstack/tdx-attest/` +- Verification: `dstack/verifier/` using `dcap-qvl` +- Event log parsing: `dstack/cc-eventlog/` ## Documentation diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f5f60267..8c5df8bd5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,27 @@ Thank you for your interest in contributing to this project! 5. Push to the branch (`git push origin feature/amazing-feature`) 6. Open a Pull Request +## Repository layout + +- `dstack/` contains the core Rust workspace and component-owned assets. Keep a + component's build, deployment, test, and API documentation next to that + component when it is not useful outside the component. +- `sdk/` contains the public language SDKs and simulator. +- `os/` contains the backend-neutral guest-OS contract, image assembly, common + guest payload, and backend implementations. A file under `os//` + must be specific to that backend. +- `docs/` contains repository-wide developer, operator, architecture, and + security documentation. Do not put a product guide at the repository root. +- `examples/` contains supported end-user examples. +- `tools/` contains cross-component developer/operator utilities. Put a script + here instead of under an OS backend when it also builds or configures the + host, deploys services, or operates on multiple components. +- `.github/` contains GitHub Actions workflows and workflow-only helpers. + +Use the narrowest owning directory. Fixture explanations and component +READMEs should stay with their fixtures/components; general guides should be +linked from the root README and live under `docs/`. + ## Commit Convention This project uses [Conventional Commits](https://www.conventionalcommits.org/). Please format your commit messages as: @@ -43,7 +64,7 @@ This project uses SPDX headers for license compliance. You should add appropriat We have a script to automatically add SPDX headers based on git blame data: ```bash -python3 scripts/add-spdx-attribution.py --file path/to/file.rs +python3 tools/add-spdx-attribution.py --file path/to/file.rs ``` Before submitting your changes, verify SPDX compliance using the [REUSE tool](https://github.com/fsfe/reuse-tool): diff --git a/LICENSES/BSD-2-Clause-Patent.txt b/LICENSES/BSD-2-Clause-Patent.txt new file mode 100644 index 000000000..31de6e498 --- /dev/null +++ b/LICENSES/BSD-2-Clause-Patent.txt @@ -0,0 +1,19 @@ +Copyright (c) + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by: + +(a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or + +(b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution. + +Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise. + +DISCLAIMER + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt new file mode 100644 index 000000000..086d3992c --- /dev/null +++ b/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,11 @@ +Copyright (c) . + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/GPL-2.0-only.txt b/LICENSES/GPL-2.0-only.txt new file mode 100644 index 000000000..17cb28643 --- /dev/null +++ b/LICENSES/GPL-2.0-only.txt @@ -0,0 +1,117 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice diff --git a/Makefile b/Makefile index 0d04c03a9..f51b05896 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,54 @@ -# SPDX-FileCopyrightText: © 2024 Phala Network +# SPDX-FileCopyrightText: © 2026 Phala Network # # SPDX-License-Identifier: Apache-2.0 -DOMAIN := local -TO := ./certs +OS_YOCTO_SUBMODULES := \ + os/yocto/deps/bitbake \ + os/yocto/deps/openembedded-core \ + os/yocto/deps/meta-yocto \ + os/yocto/deps/meta-confidential-compute \ + os/yocto/deps/meta-virtualization \ + os/yocto/deps/meta-openembedded \ + os/yocto/deps/meta-rust-bin \ + os/yocto/deps/meta-security -.PHONY: clean run all certs +.PHONY: help core core-check core-test sdk-test os os-yocto os-deps os-image os-repro-check -all: +help: + @echo "dstack monorepo targets:" + @echo " core build the Rust workspace" + @echo " core-check check the Rust workspace" + @echo " core-test test the Rust workspace with the simulator" + @echo " sdk-test run all public SDK tests" + @echo " os build the guest OS natively with the default backend" + @echo " os-yocto build the guest OS natively with Yocto" + @echo " os-deps initialize only the Yocto dependency submodules" + @echo " os-image build one production guest image in the pinned container" + @echo " os-repro-check build twice and compare reproducible outputs" -certs: ${TO} +core: + cargo build --manifest-path dstack/Cargo.toml -${TO}: - mkdir -p ${TO} - cargo run --bin certgen -- generate --domain ${DOMAIN} --output-dir ${TO} +core-check: + cargo check --manifest-path dstack/Cargo.toml --workspace -run: - $(MAKE) -C mkguest run +core-test: + ./dstack/run-tests.sh -clean: - rm -rf ${TO} +sdk-test: + cd sdk && ./run-tests.sh + +os: + ./os/build.sh + +os-yocto: + ./os/build.sh --backend yocto + +os-deps: + git submodule update --init --depth 1 -- $(OS_YOCTO_SUBMODULES) + +os-image: os-deps + cd os/yocto/repro-build && ./repro-build.sh -n + +os-repro-check: os-deps + cd os/yocto/repro-build && ./repro-build.sh diff --git a/README.md b/README.md index 2aa9ed0fe..cb3bdb6fb 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,32 @@ Deploy to a self-hosted TDX machine with the `dstackup install` -> `dstack deplo Setting up dstack on your own hardware? Start with the [self-hosted quick onboarding guide](./docs/onboarding.md) +Building or customizing the guest OS itself? Follow the [guest-OS build guide](./docs/building-guest-os.md). + ## Architecture ![Architecture](./docs/assets/arch.png) +### Repository layout + +```text +dstack/ Core services, Rust crates, host and guest runtime code +sdk/ Stable public SDK paths +os/ Guest-OS payload, image contract, and build backends +docs/ User and operator documentation +tools/ Standalone development and security tools +``` + +The currently implemented OS backend is Yocto under `os/yocto/`. Shared rootfs +payload and release assembly stay outside that backend so another builder can +be added later without duplicating them. See [`os/README.md`](./os/README.md). + +Scripts follow the same ownership boundaries: component-specific helpers stay +beside their component under `dstack/`; files installed into every guest live +in `os/common/rootfs/`; backend-neutral image tooling lives in `os/image/`; +Yocto-only helpers live in `os/yocto/scripts/`; and repository-wide standalone +utilities live in `tools/`. + Your container runs inside a Confidential VM, such as Intel TDX or AMD SEV-SNP, with optional GPU isolation via NVIDIA Confidential Computing. The CPU TEE protects application logic; the GPU TEE protects model weights and inference data. **Core components:** @@ -122,13 +144,16 @@ Apps communicate with the guest agent via HTTP over `/var/run/dstack.sock`. Use **For Operators** - [Hardware Enablement](./docs/hardware-enablement.md) - Prepare a TDX or AMD SEV-SNP host +- [AMD SEV-SNP](./docs/amd-sev-snp.md) - Image, attestation, and key-release requirements - [Self-hosted Quick Onboarding](./docs/onboarding.md) - First app on one host -- [Deployment](./docs/deployment.md) - Self-hosting on TDX hardware +- [Build the Guest OS](./docs/building-guest-os.md) - Build and verify bootable images from source +- [Deployment](./docs/deployment.md) - Self-hosting on TDX or AMD SEV-SNP hardware - [On-Chain Governance](./docs/onchain-governance.md) - Smart contract authorization - [Gateway](./docs/dstack-gateway.md) - Gateway configuration **Reference** - [App Compose Format](./docs/normalized-app-compose.md) - Compose file specification +- [Intel TDX Attestation](./docs/attestation-tdx.md) - Measurement and runtime-event verification - [Native TEE Interfaces](./docs/native-tee-interfaces.md) - Advanced compatibility with Linux TEE devices and configfs-tsm - [VMM CLI Guide](./docs/vmm-cli-user-guide.md) - Command-line reference - [Design Decisions](./docs/design-and-hardening-decisions.md) - Architecture rationale @@ -193,7 +218,7 @@ Yes. dstack runs on supported TEE-capable servers, including Intel TDX-capable h
How do users verify my deployment? -Your app exposes attestation quotes via the SDK. Users verify these quotes using [dstack-verifier](https://github.com/Dstack-TEE/dstack/tree/master/verifier), [dcap-qvl](https://github.com/Phala-Network/dcap-qvl), or the [Trust Center](https://trust.phala.com). See the [verification guide](./docs/verification.md) for details. +Your app exposes attestation quotes via the SDK. Users verify these quotes using [dstack-verifier](https://github.com/Dstack-TEE/dstack/tree/master/dstack/verifier), [dcap-qvl](https://github.com/Phala-Network/dcap-qvl), or the [Trust Center](https://trust.phala.com). See the [verification guide](./docs/verification.md) for details.
@@ -231,4 +256,7 @@ Logo and branding assets: [dstack-logo-kit](./docs/assets/dstack-logo-kit/) ## License -Apache 2.0 +The dstack-owned source, SDKs, documentation, tools, guest OS backend, and +image-assembly code are Apache-2.0. Embedded and third-party components retain +their own license declarations and notices. See file-level SPDX declarations +and [`REUSE.toml`](./REUSE.toml) for the exact scope. diff --git a/REUSE.toml b/REUSE.toml index 20dc81df6..b442c4e96 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -6,7 +6,19 @@ SPDX-PackageDownloadLocation = "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/Dstack-TEE/dstack" # Non source files [[annotations]] -path = "**/*.md" +path = [ + "*.md", + ".agent/**/*.md", + ".claude/**/*.md", + "docs/**/*.md", + "dstack/**/*.md", + "examples/**/*.md", + "sdk/**/*.md", + "tools/**/*.md", + "os/README.md", + "os/common/**/*.md", + "os/image/**/*.md", +] SPDX-FileCopyrightText = "Copyright (c) 2024-2025 The Project Contributors" SPDX-License-Identifier = "Apache-2.0" @@ -22,33 +34,32 @@ path = [ "**/tsconfig.json", "**/tsconfig.node.json", "**/tsconfig.browser.json", - "kms/auth-eth-bun/.oxlintrc.json", - "kms/auth-eth/slither.config.json", - "package-lock.json", + "dstack/kms/auth-eth-bun/.oxlintrc.json", + "dstack/kms/auth-eth/slither.config.json", "**/package-lock.json", - "kms/auth-eth/.openzeppelin/unknown-2035.json", - "kms/auth-mock/.oxlintrc.json", - "kms/auth-simple/.oxlintrc.json", - "kms/auth-simple/auth-config.example.json", + "dstack/kms/auth-eth/.openzeppelin/unknown-2035.json", + "dstack/kms/auth-mock/.oxlintrc.json", + "dstack/kms/auth-simple/.oxlintrc.json", + "dstack/kms/auth-simple/auth-config.example.json", "tools/sca/examples/heartbeat/config.json", "tools/sca/examples/hello-c/config.json", "tools/sca/examples/heartbeat/rootfs/etc/heartbeat/interval", "sdk/simulator/*.json", "sdk/go/go.sum", "sdk/go/ratls/go.sum", - "kms/dstack-app/builder/shared/builder-pinned-packages.txt", - "kms/dstack-app/builder/shared/qemu-pinned-packages.txt", - "gateway/dstack-app/builder/shared/builder-pinned-packages.txt", - "gateway/dstack-app/builder/shared/pinned-packages.txt", + "dstack/kms/dstack-app/builder/shared/builder-pinned-packages.txt", + "dstack/kms/dstack-app/builder/shared/qemu-pinned-packages.txt", + "dstack/gateway/dstack-app/builder/shared/builder-pinned-packages.txt", + "dstack/gateway/dstack-app/builder/shared/pinned-packages.txt", ] SPDX-FileCopyrightText = "Copyright (c) 2024-2025 The Project Contributors" SPDX-License-Identifier = "Apache-2.0" [[annotations]] path = [ - "gateway/templates/wg.conf", - "guest-agent/templates/metrics.tpl", - "key-provider-build/sgx_default_qcnl.conf", + "dstack/gateway/templates/wg.conf", + "dstack/guest-agent/templates/metrics.tpl", + "dstack/key-provider-build/sgx_default_qcnl.conf", ] SPDX-FileCopyrightText = "Copyright (c) 2024-2025 The Project Contributors" SPDX-License-Identifier = "Apache-2.0" @@ -57,14 +68,22 @@ SPDX-License-Identifier = "Apache-2.0" path = [ ".cursorrules", ".mailmap", - "**/.gitignore", - "**/.npmignore", + ".gitignore", + "dstack/**/.gitignore", + "dstack/**/.npmignore", + "docs/**/.gitignore", + "examples/**/.gitignore", + "os/common/**/.gitignore", + "os/image/**/.gitignore", + "sdk/**/.gitignore", + "sdk/**/.npmignore", + "tools/**/.gitignore", ] SPDX-FileCopyrightText = "Copyright (c) 2024-2025 The Project Contributors" SPDX-License-Identifier = "Apache-2.0" [[annotations]] -path = "basefiles/*" +path = "os/common/rootfs/**" SPDX-FileCopyrightText = "Copyright (c) 2024-2025 The Project Contributors" SPDX-License-Identifier = "Apache-2.0" @@ -74,24 +93,45 @@ path = [ "dstack_Technical_Charter_Final_10-17-2025.pdf", "sdk/simulator/quote.hex", "sdk/simulator/attestation.bin", - "ra-tls/assets/tdx_quote", - "cc-eventlog/samples/ccel.bin", - "cc-eventlog/samples/tpm_eventlog.bin", - "tpm-attest/tests/tpm_quote_sample.bin", - "tpm-qvl/certs/gcp-root-ca.pem", - "dstack-attest/tests/nitro_attestation.bin", - "dstack-attest/tests/nitro_attestation_dbg.bin", - "dstack-attest/tests/sev_snp_attestation.bin", - "dstack-attest/tests/sev_snp_ask.pem", - "dstack-attest/tests/sev_snp_vcek.pem", - "nsm-attest/tests/nitro_attestation.bin", - "nsm-qvl/tests/nitro_attestation.bin", - "nsm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem", - "tpm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem", + "dstack/ra-tls/assets/tdx_quote", + "dstack/cc-eventlog/samples/ccel.bin", + "dstack/cc-eventlog/samples/tpm_eventlog.bin", + "dstack/tpm-attest/tests/tpm_quote_sample.bin", + "dstack/tpm-qvl/certs/gcp-root-ca.pem", + "dstack/dstack-attest/tests/nitro_attestation.bin", + "dstack/dstack-attest/tests/nitro_attestation_dbg.bin", + "dstack/dstack-attest/tests/sev_snp_attestation.bin", + "dstack/dstack-attest/tests/sev_snp_ask.pem", + "dstack/dstack-attest/tests/sev_snp_vcek.pem", + "dstack/nsm-attest/tests/nitro_attestation.bin", + "dstack/nsm-qvl/tests/nitro_attestation.bin", + "dstack/nsm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem", + "dstack/tpm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem", ] SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" +# Backend-neutral OS contract + +[[annotations]] +path = "os/spec/*.json" +SPDX-FileCopyrightText = "Copyright (c) 2026 The Project Contributors" +SPDX-License-Identifier = "Apache-2.0" + +# dstack-owned OS backend + +[[annotations]] +path = "os/yocto/**" +SPDX-FileCopyrightText = "Copyright (c) Hashforest Technology LLC" +SPDX-License-Identifier = "Apache-2.0" + +# Legacy cross-component tools kept outside the Yocto backend. + +[[annotations]] +path = "tools/vm-runner/**" +SPDX-FileCopyrightText = "Copyright (c) Hashforest Technology LLC" +SPDX-License-Identifier = "Apache-2.0" + # Artworks @@ -107,7 +147,7 @@ SPDX-License-Identifier = "Apache-2.0" # Scripts with SPDX-like content (false positive prevention) [[annotations]] -path = "scripts/add-spdx-attribution.py" +path = "tools/add-spdx-attribution.py" SPDX-FileCopyrightText = "© 2025 Phala Network " SPDX-License-Identifier = "Apache-2.0" precedence = "override" @@ -115,19 +155,19 @@ precedence = "override" # Vendor code [[annotations]] -path = "kms/auth-eth/lib/openzeppelin-contracts-upgradeable/**" +path = "dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable/**" SPDX-FileCopyrightText = "Copyright (c) 2016-2025 Zeppelin Group Ltd" SPDX-License-Identifier = "MIT" precedence = "override" [[annotations]] -path = "kms/auth-eth/lib/openzeppelin-foundry-upgrades/**" +path = "dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades/**" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "MIT" precedence = "override" [[annotations]] -path = "kms/auth-eth/lib/forge-std/**" +path = "dstack/kms/auth-eth/lib/forge-std/**" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "Apache-2.0" precedence = "override" @@ -135,7 +175,7 @@ precedence = "override" # Generated files [[annotations]] -path = "kms/auth-eth/typechain-types/**" +path = "dstack/kms/auth-eth/typechain-types/**" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" @@ -149,11 +189,6 @@ path = "**/*.lockb" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" -[[annotations]] -path = "**/*.lockb" -SPDX-FileCopyrightText = "NONE" -SPDX-License-Identifier = "CC0-1.0" - [[annotations]] path = "**/*.snap" SPDX-FileCopyrightText = "NONE" @@ -170,40 +205,40 @@ SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] -path = "gateway/assets/*" +path = "dstack/gateway/assets/*" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] -path = "guest-api/src/generated/*" +path = "dstack/guest-api/src/generated/*" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] -path = "dstack-util/tests/fixtures/*" +path = "dstack/dstack-util/tests/fixtures/*" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] -path = "verifier/fixtures/*" +path = "dstack/verifier/fixtures/*" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] -path = "verifier/builder/shared/*.txt" +path = "dstack/verifier/builder/shared/*.txt" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] -path = "guest-agent/fixtures/*" +path = "dstack/guest-agent/fixtures/*" SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" [[annotations]] path = [ - "gateway/test-run/e2e/certs/*", - "gateway/test-run/e2e/configs/*", - "gateway/test-run/e2e/pebble-config.json", + "dstack/gateway/test-run/e2e/certs/*", + "dstack/gateway/test-run/e2e/configs/*", + "dstack/gateway/test-run/e2e/pebble-config.json", ] SPDX-FileCopyrightText = "NONE" SPDX-License-Identifier = "CC0-1.0" diff --git a/basefiles/tdx-attest.conf b/basefiles/tdx-attest.conf deleted file mode 100644 index d7c6361ae..000000000 --- a/basefiles/tdx-attest.conf +++ /dev/null @@ -1 +0,0 @@ -port=4050 \ No newline at end of file diff --git a/docs/amd-sev-snp-review-readiness.md b/docs/amd-sev-snp-review-readiness.md deleted file mode 100644 index 99c685731..000000000 --- a/docs/amd-sev-snp-review-readiness.md +++ /dev/null @@ -1,201 +0,0 @@ -# AMD SEV-SNP Review Readiness - -This branch adds AMD SEV-SNP support and now includes a controlled, explicitly opt-in KMS key/cert release gate for SNP. - -## Current review boundary - -Implemented and intended for review: - -- AMD SEV-SNP evidence plumbing in the v1 attestation format. -- SNP report verification with AMD Milan/Genoa/Turin ARK/ASK/VCEK chain verification (built-in ARK/ASK roots per product; Bergamo/Siena parts are canonicalized under the Genoa KDS endpoint). -- Report-data challenge binding and fail-closed report policy checks. -- SNP launch-measurement recomputation from OVMF/kernel/initrd/cmdline inputs. -- KMS SNP `BootInfo` construction from verified report measurement, chip id, launch inputs, TCB status, and advisory ids. -- Auth-policy evaluation through the existing KMS auth flow. -- Controlled SNP key/cert release guarded by both external auth policy and local KMS config. -- VMM-provided SNP launch inputs in `.sys-config.json` so KMS self/app auth can recompute the same launch measurement used by QEMU. -- Onboarding attestation-info reporting for SNP identity fields. -- VMM SNP launch path, selected either by host auto-detection (`/proc/cpuinfo` `sev_snp` CPU flag) or by an explicit `platform = "amd-sev-snp"` pin. - -Default posture: - -- SNP app key release, KMS/root/temp CA key release, and app certificate release are still disabled by default. -- Operators must explicitly set `core.sev_snp_key_release = true` before any SNP `BootInfo` can release sensitive material. -- The self-authorized `GetTempCaCert` path is gated per-RPC, not at startup: it runs `ensure_self_key_release_allowed` against the KMS's own self `BootInfo`. With the production default `enforce_self_authorization = true`, the KMS self-attests and any SNP self `BootInfo` must clear the same release gate as app requests. `enforce_self_authorization = false` is a dev/test-only escape hatch (it logs a startup warning, not a hard error); in that mode the self `BootInfo` is `None`, so the self-release gate is skipped — do not use it in production TEE deployments. -- Even with the local KMS gate enabled, the existing auth API must first allow the verified SNP `BootInfo` for the app/KMS identity. - -## Fail-closed policy summary - -- `platform` selects the guest TEE: omitted or the legacy `auto` value auto-detects the host TEE from `/proc/cpuinfo` (the `sev_snp` CPU flag selects AMD SEV-SNP; otherwise it falls back to TDX), and operators can pin `platform = "amd-sev-snp"` or `platform = "tdx"` to override detection. SNP key release stays fail-closed regardless of how the platform is selected: an auto-detected SNP launch still cannot release sensitive material until the SNP release gate below is explicitly enabled. -- SNP launch measurement is recomputed from the self-contained VMM launch inputs and compared to the hardware-verified report measurement. -- SNP `BootInfo.tcb_status` is verifier-derived from signed AMD SNP report TCB fields: - - `UpToDate` only when current/reported/committed/launch TCB versions all match. - - `OutOfDate` otherwise. -- SNP advisory ids are propagated from verifier output into `BootInfo`; currently this list is explicit and empty because the AMD report/VCEK evidence used here does not carry a direct advisory-list field. -- `auth-simple` defaults remain strict: only `UpToDate` is accepted and any advisory id is denied unless explicitly allowlisted. -- The local KMS release gate is intentionally only an operator opt-in switch: - - `core.sev_snp_key_release = false` by default. - - TCB/advisory policy is not duplicated in KMS config; it is decided by the auth API using the verified `BootInfo`. - -Example opt-in gate: - -```toml -[core] -sev_snp_key_release = true -``` - -Sensitive release surfaces using this gate: - -- `GetAppKey`: app disk/env/k256 key material. -- `GetKmsKey`: temp CA key plus root CA/k256 key material for authorized KMS transfer. -- `SignCert`: app certificate chain signing. -- `GetTempCaCert`: temp CA material for self-authorized KMS instances. - -## Live golden-vector proof - -The ignored live regression test cross-checks dstack's pure Rust SNP measurement recomputation against `sev-snp-measure` on the SNP-capable host. - -> Status: the captured vector below is **stale**. It predates the move of SNP app identity from the kernel cmdline into the MrConfigV3 `HOST_DATA` binding, so the recorded `sev_snp_measurement` no longer matches the current recomputation. It must be regenerated on an SNP host before relying on it as proof. The current end-to-end live evidence is the SNP E2E smoke section below, which exercises the updated HOST_DATA-bound path through real key release. - -Command: - -```bash -cargo test -p dstack-kms --all-features recomputation_matches_sev_snp_measure_live_golden_vector -- --ignored --nocapture -``` - -Last captured vector (STALE — regenerate before citing as proof): - -```text -DSTACK_SEV_SNP_MEASURE_GOLDEN_VECTOR_BEGIN -utc=2026-06-02T19:49:14Z -host=dedicated-m24-fork -uname=Linux dedicated-m24-fork 6.11.0-rc3-snp-host-85ef1ac03941 #2 SMP Sat May 3 11:42:34 EDT 2025 x86_64 GNU/Linux -sev_snp_measure=/usr/local/bin/sev-snp-measure -sev_snp_measure_version=sev-snp-measure 0.0.10 -ovmf_path=/opt/AMDSEV/usr/local/share/qemu/OVMF.fd -ovmf_sha256=67e7a7027437823e9c166a60d00666d5d5391e13050488cad5cc2acd913fab4a -kernel_fixture_sha256=3f73f96a321b35a4c5561b05cfa6e9b5c573159380d37abe76f9a8ebe113a72e -initrd_fixture_sha256=e8790816224329cd76675c2aba4e62e885b5a4e0ec056227da70e775191d6d56 -vcpus=2 -vcpu_type=EPYC-v4 -guest_features=0x1 -append=console=ttyS0 loglevel=7 -sev_snp_measurement=requires-refresh-after-mr-config-v3-host-data-binding -cargo_live_test=cargo test -p dstack-kms --all-features recomputation_matches_sev_snp_measure_live_golden_vector -- --ignored --nocapture -cargo_live_test_result=stale after SNP app identity moved from cmdline to HOST_DATA -DSTACK_SEV_SNP_MEASURE_GOLDEN_VECTOR_END -``` - -## Guest attestation proof - -A prior SNP guest smoke proof confirmed the guest kernel exposed SEV-SNP report support and could produce a report containing the expected challenge bytes. - -```text -Memory Encryption Features active: AMD SEV SEV-ES SEV-SNP -SEV: SNP running at VMPL0. -sev-guest sev-guest: Initialized SEV guest driver (using vmpck_id 0) -DSTACK_SEV_SNP_ATTESTATION_PROOF_BEGIN -source=configfs-tsm -report_size=1184 -report_data_offset=80 -report_contains_expected_report_data=true -DSTACK_SEV_SNP_ATTESTATION_PROOF_END -``` - -## Manual dstack E2E smoke status - -An additional manual smoke was attempted on the SNP host (`chris@173.234.27.162`) using the PR branch, release-built `dstack-vmm`/`supervisor`/`dstack-kms`, QEMU 10.0.2, and the SNP-capable OVMF at `/opt/AMDSEV/usr/local/share/qemu/OVMF.fd`. The reusable version of that smoke is checked in at `test-scripts/snp-e2e-smoke.sh` for follow-up debugging on SNP hosts. - -That smoke exposed and fixed several VMM/KMS-auth integration issues before the guest reached KMS: - -- `.sys-config.json` did not include the `sev_snp_measurement` launch input document needed by KMS SNP `BootInfo` recomputation. -- The VMM launch path required `metadata.json.rootfs_hash`, while the released `dstack-0.5.11` images carry the rootfs hash in `dstack.rootfs_hash=...` on the kernel cmdline. -- The VMM SNP QEMU path now uses the SNP measurement CPU model (`EPYC-v4`) and confidential virtio PCI options (`disable-legacy=on,iommu_platform=true`) for SNP-launched virtio devices, matching the host's working SNP launch posture more closely. - -After those fixes, the manual smoke progressed through full dstack-managed SNP guest boot and KMS self-bootstrap on the known-good remote host. Additional smoke/debug fixes made the host/KMS side reach the app-key boundary: - -- Minimal guest boot now keeps DNS usable when `systemd-resolved`/`chronyd` are unavailable early in smoke boots and detects `sev-guest` before trying the TDX guest module. -- SNP guests verify the SNP `HOST_DATA` value against the attached MrConfigV3 document instead of using TDX-only `mr_config_id`. -- Configfs TSM report collection falls back to the SEV-SNP extended-report ioctl when configfs does not carry certificate collateral. -- If verifier-side evidence still lacks ASK/VCEK collateral, the verifier can fetch AMD KDS ARK/ASK/VCEK using the report `chip_id` and reported TCB, then verify the signed report fail-closed. -- KMS measurement recomputation now uses the image's original kernel cmdline for SNP launch measurement, while app identity is bound by MrConfigV3/HOST_DATA instead of appended cmdline fields. -- VMM now passes the image's split `measurement.snp.cbor` material plus per-launch SNP fields through the guest to KMS; KMS no longer needs a single locally configured `ovmf_path`, so different image/OVMF versions can be verified by their self-contained launch inputs. -- SNP `BootInfo.os_image_hash` is the unified image digest (`sha256(sha256sum.txt)`). The `measurement.snp.cbor` entry in `sha256sum.txt` commits to the cmdline, kernel/initrd hashes, and OVMF hash/sections while excluding per-deployment values like vCPU count/model and guest features. - -Latest sanitized remote smoke result with PR-built host binaries and a coherent `MACHINE = "sev-snp"` guest image: - -```text -remote_host=chris@173.234.27.162 -host_kernel=Linux 6.11.0-rc3-snp-host-85ef1ac03941 -qemu_version=10.0.2 -ovmf_sha256=67e7a7027437823e9c166a60d00666d5d5391e13050488cad5cc2acd913fab4a -image=dstack-dev-0.6.0 -platform=amd-sev-snp -image_kernel=Linux 6.18.24-dstack with CONFIG_AMD_MEM_ENCRYPT=y, CONFIG_SEV_GUEST=y, CONFIG_TSM_REPORTS=y -kms_guest=booted SNP Linux/userspace and started dstack-kms -kms_marker=SNP_KMS_CONTAINER_STARTED / KMS runtime ready -kds_base_url=enabled for smoke via DSTACK_SNP_SMOKE_KDS_BASE_URL=https://cors.litgateway.com/https://kdsintf.amd.com/vcek/v1 -strict_tcb_probe=denied_as_expected by auth API with tcb_status is not allowed -success_probe=GetTempCaCert HTTP 200; GetAppKey HTTP 200; SignCert HTTP 200; app container started -smoke_result=SNP E2E smoke success -no_secret_material_logged=true -``` - -This means the PR has live SNP report proof, live golden-vector measurement proof, release-gate unit/integration coverage, and hardware smoke proof through dstack-managed SNP KMS boot, auth-API strict TCB denial, app guest key release, and app container startup. The fresh-box smoke now reaches Linux/userspace, `SNP_KMS_CONTAINER_STARTED`, `GetTempCaCert`, `GetAppKey`, `SignCert`, and app container startup when using a coherent **SNP** `meta-dstack` image. During the smoke, AMD KDS throttling was worked around by explicitly routing AMD KDS collateral fetches through the smoke-level `DSTACK_SNP_SMOKE_KDS_BASE_URL=https://cors.litgateway.com/https://kdsintf.amd.com/vcek/v1`; the smoke writes this value to the top-level KMS `core.amd_kds_base_url` configuration. This is an AMD-KDS-compatible base URL; requests append relative KDS paths such as `/Milan/cert_chain` or `/Milan/?...`. Host/KMS binaries must match PR #703, guest-side `dstack-util`/`dstack-attest` must include the PR cert-chain/KDS fallback, and the Yocto image must be built with `MACHINE = "sev-snp"` so the guest kernel includes AMD memory-encryption/SNP support. A coherent PR image built with the default `tdx` machine produced a `6.18.24-dstack` kernel with `# CONFIG_AMD_MEM_ENCRYPT is not set`; controlled QEMU tests showed that kernel resets immediately after OVMF loads kernel/initrd, while SNP-capable kernels boot the same QEMU/OVMF path to Linux/SNP markers. - -### Fresh SNP host / image requirements - -The checked-in smoke is enough to reproduce the current boundary on a compatible SNP host, but reviewers should treat the guest image/kernel/userspace as part of the test matrix: - -- Known-good host for reaching KMS and app `dstack-prepare.sh`: `chris@173.234.27.162` with QEMU 10.0.2, the SNP-capable OVMF above, and a coherent `dstack-dev-0.6.0` guest image built with `MACHINE = "sev-snp"`. -- Released images that do not carry PR #703 guest-side `dstack-util`/`dstack-attest` may reject SNP evidence before the newer PR fallback paths can help. -- A coherent PR #703 image must be built as an SNP image, not with `meta-dstack`'s default `tdx` machine. The default TDX build can emit a kernel without `CONFIG_AMD_MEM_ENCRYPT`, which fails before Linux serial output under SNP. -- On the same remote host/QEMU/OVMF, a minimal SNP initramfs booted SNP-capable kernels (`6.11.0-rc3-snp-host`, `6.9.0-rc7-snp-host`, and the `MACHINE = "sev-snp"` `6.18.24-dstack` kernel) to Linux/SNP markers, while the default-TDX `6.18.24-dstack` kernel reset immediately after OVMF loaded kernel/initrd. This isolates that failure to the guest kernel config, not PSP firmware, KMS/auth policy, command line, virtio wiring, or basic host SNP enablement. - -Practical implication for reviewers/testers on a fresh box: - -1. Install/use an AMDSEV QEMU 10.x build and the matching SNP-capable OVMF. -2. Build the PR binaries with `cargo build --release -p dstack-vmm -p supervisor -p dstack-kms`. -3. Run `test-scripts/snp-e2e-smoke.sh` unchanged and first confirm it reaches `SNP_KMS_CONTAINER_STARTED`; if AMD KDS throttles the lab host, set `DSTACK_SNP_SMOKE_KDS_BASE_URL` to a trusted AMD-KDS-compatible mirror/cache base URL such as `https://mirror.example.com/vcek/v1` (or, for a path-prefix relay, `https://cors.litgateway.com/https://kdsintf.amd.com/vcek/v1`) and rerun. The lab success above also used `DSTACK_SNP_SMOKE_ALLOW_OUT_OF_DATE_TCB=1` because the current SNP lab host reports `OutOfDate`; production auth policy should keep accepting only `UpToDate` and deny any advisory id unless explicitly allowlisted. -4. For full `SNP_APP_CONTAINER_STARTED` / `GetAppKey` success, use or publish a coherent `meta-dstack` guest image whose kernel, modules, initramfs, rootfs, verity metadata, and guest userspace include the same PR #703 `dstack-util`/`dstack-attest` SNP cert-chain/KDS fallback code. The reproducible path is to build `meta-dstack` with its `dstack` submodule checked out to this PR branch, for example: - - ```bash - git clone https://github.com/Dstack-TEE/meta-dstack.git - cd meta-dstack - git submodule update --init --recursive --depth 1 - cd dstack - git fetch https://github.com/clawdbot-glitch003/dstack.git feat/amd-sev-snp-conversion - git checkout -B feat/amd-sev-snp-conversion FETCH_HEAD - cd .. - source dev-setup ./bb-build - sed -i 's/^MACHINE ??= .*/MACHINE = "sev-snp"/' ./bb-build/conf/local.conf - FLAVORS=dev make dist DIST_DIR=$PWD/images BB_BUILD_DIR=$PWD/bb-build - # Use the resulting dstack-dev image directory with: - # DSTACK_SNP_SMOKE_IMAGE_NAME= - ``` - - Do not try to inject only a replacement `dstack-util` into the stock image; that experiment changed the initramfs/measurement enough to regress boot. -5. Only after the baseline smoke reaches the app success marker should testers swap the simple app workload for Chipotle. - -If the smoke stops after `EFI stub: Loaded initrd ...` with `cpus are not resettable`, use a host/image/kernel that is known to boot dstack under SNP before debugging app-level behavior. If it reaches `Requesting app keys from KMS` and fails with AMD KDS `HTTP 429`, use the smoke KDS base URL hook above; if it fails with missing cert-chain/collateral without KDS base URL evidence, rebuild/use a coherent PR guest image rather than changing KMS release policy. - -## Validation commands - -Run locally for this review-ready staging branch: - -```bash -bash -n test-scripts/snp-e2e-smoke.sh -cargo fmt --all -cargo test -p dstack-kms --all-features -cargo test -p dstack-attest --all-features -cargo test -p dstack-vmm --all-features -cargo test -p ra-rpc --all-features -cargo check --workspace --all-features -cargo clippy --workspace --all-features -- -D warnings --allow unused_variables -git diff --check -cd kms/auth-simple && bun install && bun run check -``` - -## Remaining production follow-up - -The release gate is controlled and production-oriented, but AMD advisory/revocation collateral is still limited by the evidence source available here: SNP reports/VCEKs do not directly carry an advisory list, so `advisory_ids` currently propagates as an explicit empty list. Future collateral fetchers can populate this field; auth policy should deny those advisories unless each one is explicitly allowlisted. diff --git a/docs/amd-sev-snp.md b/docs/amd-sev-snp.md new file mode 100644 index 000000000..a1320ce98 --- /dev/null +++ b/docs/amd-sev-snp.md @@ -0,0 +1,151 @@ +# AMD SEV-SNP Support + +This document describes how dstack uses AMD SEV-SNP on self-hosted bare-metal +systems. AMD SEV-SNP support is currently **experimental**; Intel TDX remains +the production bare-metal path. + +For platform firmware, kernel, QEMU, and OVMF preparation, start with +[Hardware Enablement](./hardware-enablement.md). This document covers the +dstack-specific image, installation, attestation, and key-release requirements. + +## Supported image line + +dstack OS 0.6.0 and later use one `dstack-` guest image for both Intel +TDX and AMD SEV-SNP. The unified Yocto machine includes both platform kernel +feature sets and detects the active TEE at runtime. Do not change the Yocto +`MACHINE` to a platform-specific value. + +Guest OS releases are split at the monorepo boundary: + +- versions below 0.6.0 are archived in + [`Dstack-TEE/meta-dstack`](https://github.com/Dstack-TEE/meta-dstack/releases) + under tags such as `v0.5.11`; +- versions 0.6.0 and later are in + [`Dstack-TEE/dstack`](https://github.com/Dstack-TEE/dstack/releases?q=guest-os-v) + under tags such as `guest-os-v0.6.0`. + +The 0.5.x images are legacy TDX images, not the current unified SEV-SNP image +line. Use a 0.6.0-or-later image for SEV-SNP. The image must include +`digest.txt`, `sha256sum.txt`, and the SNP measurement material. + +## Host requirements + +The host must provide: + +- an SEV-SNP-capable AMD processor and current platform firmware; +- SEV-SNP and the Reverse Map Table (RMP) enabled by the host kernel; +- `/dev/sev`; +- a QEMU and OVMF build with SEV-SNP support. + +After following the host platform's enablement procedure, check: + +```bash +test -e /dev/sev +sudo dmesg | grep -e SEV-SNP -e RMP +cat /sys/module/kvm_amd/parameters/sev_snp +``` + +The last command should print `Y`. `dstackup` checks `/dev/sev`, but that +preflight does not replace firmware, kernel, QEMU, or OVMF validation. + +## Install dstack on an SNP host + +Pull a current unified image and select the platform explicitly: + +```bash +VERSION=0.6.0 +sudo dstackup image pull --version "$VERSION" +sudo dstackup install --platform amd-sev-snp --image "dstack-$VERSION" +sudo dstackup status +``` + +The default `--platform auto` mode also selects SEV-SNP when the host CPU flags +advertise `sev_snp`. Explicit selection is preferable while commissioning a +host because it fails immediately when the required SNP device is absent. + +Unlike the TDX path, SEV-SNP does not use the local SGX key provider. + +## Attestation and image identity + +The guest collects an SNP attestation report through Linux configfs-tsm when +available, with `/dev/sev-guest` extended-report collection as the fallback. +See [Native TEE Interfaces](./native-tee-interfaces.md) before exposing either +kernel interface directly to an application container. + +Verification is fail-closed and includes: + +1. the AMD ARK/ASK/VCEK certificate chain and report signature; +2. the requested `REPORT_DATA` challenge binding and SNP policy fields; +3. the launch `MEASUREMENT` recomputed from the VMM's firmware, kernel, + initramfs, command line, and launch inputs; +4. the MrConfigV3 `HOST_DATA` application-identity binding; +5. the unified OS image identity, `sha256(sha256sum.txt)`, which must match + `digest.txt` and the SNP measurement document. + +The verifier supports the AMD Milan, Genoa, and Turin KDS product families. +Bergamo and Siena are handled through AMD's canonical Genoa KDS product path. + +`BootInfo.tcb_status` is `UpToDate` only when the current, reported, committed, +and launch TCB versions agree; otherwise it is `OutOfDate`. Authorization +policy should remain strict for non-up-to-date TCB values. The verifier +currently reports an explicit empty advisory-ID list because the SNP report +and VCEK evidence do not directly carry an advisory list. + +## KMS key-release policy + +SEV-SNP key and certificate release has two independent gates: + +1. the external KMS authorization policy must accept the verified `BootInfo`; +2. the local KMS operator must explicitly enable SNP release. + +The local gate is disabled by default: + +```toml +[core] +sev_snp_key_release = false +``` + +After the host, image, attestation, and external authorization policy have been +validated, enable it deliberately in the KMS configuration: + +```toml +[core] +sev_snp_key_release = true +``` + +This gate covers application keys, KMS key transfer, application certificate +signing, and self-authorized temporary CA material. Enabling it does not bypass +the external authorization decision. Keep `enforce_self_authorization = true` +for production TEE deployments. + +## AMD KDS collateral + +The verifier obtains AMD certificate collateral from the built-in AMD KDS URL +when the attestation evidence does not already contain the required chain. An +operator can set an AMD-KDS-compatible mirror or cache: + +```toml +[core] +amd_kds_base_url = "https://mirror.example.com/vcek/v1" +``` + +Leave the value empty to use the built-in default. A custom endpoint is part of +the verification trust and availability boundary: use a controlled mirror, +preserve TLS validation, and do not make verification succeed without valid +AMD signatures. + +## Troubleshooting + +- **`/dev/sev` is missing:** finish host firmware/kernel enablement before + running `dstackup install`. +- **The guest resets before Linux starts:** verify that the selected QEMU, + OVMF, and unified dstack OS image all support SNP. Do not debug KMS policy + until the guest boots reliably. +- **Image identity files are missing:** use a 0.6.0-or-later unified image. + `dstackup install` rejects an SNP image without `digest.txt`. +- **KDS requests fail:** check host time, DNS, outbound HTTPS, and KDS or mirror + availability. Do not disable certificate or signature verification. +- **Attestation succeeds but key release fails:** check both the external auth + response and `core.sev_snp_key_release`; either gate can deny the request. +- **TCB is `OutOfDate`:** update platform firmware and re-evaluate the reported, + committed, current, and launch TCB versions before changing auth policy. diff --git a/attestation.md b/docs/attestation-tdx.md similarity index 81% rename from attestation.md rename to docs/attestation-tdx.md index c10232cd8..f0f433423 100644 --- a/attestation.md +++ b/docs/attestation-tdx.md @@ -1,4 +1,4 @@ -# TEE Attestation Guide for dstack Applications +# Intel TDX Attestation Guide for dstack Applications This document outlines the process of verifying the authenticity and integrity of data produced by dstack Applications running within Intel TDX environments. @@ -33,26 +33,30 @@ RTMR3 differs as it contains runtime information like compose hash and instance ### 2.2. Determining expected MRs MRTD, RTMR0, RTMR1, and RTMR2 correspond to the image. dstack OS builds all related software from source. -Build version v0.5.4 using these commands: +Build the exact image revision you intend to verify. See +[Build the dstack guest OS](./building-guest-os.md) for prerequisites and the +reproducible build workflow. At a high level: + ```bash -git clone https://github.com/Dstack-TEE/meta-dstack.git -cd meta-dstack/ -git checkout f7c795b76faa693f218e1c255007e3a68c541d79 -git submodule update --init --recursive -cd repro-build && ./repro-build.sh -n +git clone https://github.com/Dstack-TEE/dstack.git +cd dstack +git checkout +make os-image ``` -The resulting dstack-0.5.4.tar.gz contains: +The resulting `dstack-.tar.gz` contains: - ovmf.fd: virtual firmware - bzImage: kernel image - initramfs.cpio.gz: initrd -- rootfs.img.verity: root filesystem +- rootfs.img.parted.verity: partitioned dm-verity root filesystem - metadata.json: image metadata, including kernel boot cmdline -Calculate image MRs using [dstack-mr](dstack-mr/): +Calculate image MRs using [dstack-mr](../dstack/dstack-mr/): ```bash -cargo run --manifest-path ../dstack/Cargo.toml --bin dstack-mr measure -c 4 -m 4G dstack-0.5.4/metadata.json +VERSION=0.6.0 # replace with the image version being verified +cargo run --manifest-path dstack/Cargo.toml --bin dstack-mr measure \ + -c 4 -m 4G "dstack-$VERSION/metadata.json" ``` Once these verification steps are completed successfully, the report_data contained in the verified quote can be considered authentic and trustworthy. @@ -63,7 +67,7 @@ To verify dstack App data trustworthiness: - Review source code for correctness and safety. - Build image from source. -- Calculate MRTD, RTMR0, RTMR1, and RTMR2 values using [dstack-mr](https://github.com/kvinwang/dstack-mr). +- Calculate MRTD, RTMR0, RTMR1, and RTMR2 values using [dstack-mr](https://github.com/Dstack-TEE/dstack/tree/master/dstack/dstack-mr). - Verify quote measurements: - Confirm MRTD, RTMR0, RTMR1, and RTMR2 match pre-calculated values. - Verify RTMR3 matches the event log replay result. diff --git a/docs/auth-simple-operations.md b/docs/auth-simple-operations.md index ffd387a80..416df9e4c 100644 --- a/docs/auth-simple-operations.md +++ b/docs/auth-simple-operations.md @@ -296,5 +296,5 @@ Expected responses: ## See Also - [Deployment Guide](./deployment.md) - Initial setup -- [auth-simple README](../kms/auth-simple/README.md) - Developer reference +- [auth-simple README](../dstack/kms/auth-simple/README.md) - Developer reference - [On-Chain Governance](./onchain-governance.md) - Smart contract-based alternative diff --git a/docs/bridge-networking.md b/docs/bridge-networking.md index 4dac27bd0..a96d5c9dc 100644 --- a/docs/bridge-networking.md +++ b/docs/bridge-networking.md @@ -93,7 +93,7 @@ sudo apt install -y dnsmasq Install the DHCP notification script (notifies VMM when a VM gets an IP so port forwarding can be established): ```bash -sudo cp scripts/dhcp-notify.sh /usr/local/bin/dhcp-notify.sh +sudo cp dstack/scripts/dhcp-notify.sh /usr/local/bin/dhcp-notify.sh sudo chmod +x /usr/local/bin/dhcp-notify.sh ``` diff --git a/docs/building-guest-os.md b/docs/building-guest-os.md new file mode 100644 index 000000000..f783ac327 --- /dev/null +++ b/docs/building-guest-os.md @@ -0,0 +1,220 @@ +# Build the dstack guest OS + +This guide builds the bootable dstack guest-OS release artifacts from source. +It is for OS developers, release maintainers, and operators who want a custom +image. You do **not** need to build an image for normal self-hosted onboarding: +`dstackup install` downloads and verifies a published guest-OS release by +default. + +## What the build produces + +The default `prod` build produces: + +- a bare-metal/CVM bundle for Intel TDX and, when the SEV firmware artifact is + available, AMD SEV-SNP; +- a UKI disk-image bundle for the GCP confidential-VM boot path; +- dm-verity rootfs data, launch-measurement material, checksums, and the unified + `digest.txt` OS identity. + +Yocto is currently the only implemented OS backend. Backend-independent rootfs +payload, artifact contract, measurement, and release packaging live outside +`os/yocto/`; see [`../os/README.md`](../os/README.md). + +## Prerequisites + +Use an x86-64 Linux host with: + +- Git; +- Docker Engine, usable by the current user; +- outbound HTTPS access for Git, Yocto source archives, and Rust crates; +- substantial free disk space for Yocto downloads, work directories, and + shared-state cache. + +TEE hardware is not required to build the image. It is required only when you +boot and attest the resulting image on the corresponding platform. + +Check the basics before starting: + +```bash +docker version +git --version +df -h . +``` + +## Quick build + +From a fresh checkout: + +```bash +git clone https://github.com/Dstack-TEE/dstack.git +cd dstack +make os-image +``` + +`make os-image` initializes only the eight Yocto dependency submodules and +runs one complete production image build in the pinned Ubuntu builder +container. It is equivalent to: + +```bash +git submodule update --init --depth 1 -- \ + os/yocto/deps/bitbake \ + os/yocto/deps/openembedded-core \ + os/yocto/deps/meta-yocto \ + os/yocto/deps/meta-confidential-compute \ + os/yocto/deps/meta-virtualization \ + os/yocto/deps/meta-openembedded \ + os/yocto/deps/meta-rust-bin \ + os/yocto/deps/meta-security + +cd os/yocto/repro-build +./repro-build.sh -n +``` + +The first build downloads and compiles the complete Yocto toolchain and guest +userspace, so it is much slower than an incremental rebuild. The `-n` option +means “build once”; it does not skip BitBake or image assembly. + +## Outputs + +Release archives are written under: + +```text +os/yocto/repro-build/dist/ +├── dstack-.tar.gz +├── dstack--uki.tar.gz +└── reproduce.sh +``` + +`reproduce.sh` is emitted when the source tree is clean. The unpacked build +tree and caches remain under `os/yocto/repro-build/build-a/`. + +The bare-metal archive includes the kernel, initramfs, OVMF firmware, +partitioned dm-verity rootfs, platform measurement CBOR files, +`sha256sum.txt`, `digest.txt`, and `metadata.json`. The UKI archive +includes the bootable `disk.raw` plus its identity and measurement files. + +Inspect and verify an archive with: + +```bash +mkdir -p /tmp/dstack-image +tar -xzf os/yocto/repro-build/dist/dstack-.tar.gz \ + -C /tmp/dstack-image +cd /tmp/dstack-image/dstack- +sha256sum -c sha256sum.txt +test "$(sha256sum sha256sum.txt | awk '{print $1}')" = "$(cat digest.txt)" +``` + +## Build both production and development flavors + +Production is the default. To build both variants once: + +```bash +cd os/yocto/repro-build +RELEASE_FLAVORS="prod dev" ./repro-build.sh -n +``` + +The development archive is named `dstack-dev-.tar.gz` and records +`"is_dev": true` in `metadata.json`. + +## Check reproducibility + +For a release candidate, omit `-n`: + +```bash +make os-repro-check +``` + +This builds independent `build-a` and `build-b` trees and compares the +release-relevant output allowlist. It takes roughly twice the resources of a +single build. Remove both ignored build trees if you specifically need a +from-scratch comparison: + +```bash +rm -rf os/yocto/repro-build/build-a \ + os/yocto/repro-build/build-b \ + os/yocto/repro-build/dist +make os-repro-check +``` + +## Incremental backend development + +The reproducible wrapper is the recommended release path. On a host with the +packages listed in `os/yocto/repro-build/Dockerfile.repro`, the generic +backend entrypoint can also be used directly from the repository root: + +```bash +./os/build.sh \ + --backend yocto \ + --flavors prod \ + --build-dir "$PWD/os/yocto/bb-build" +``` + +This keeps the native BitBake cache in `os/yocto/bb-build/` and writes +assembled images under the repository-root `images/` directory. Build both +flavors with `--flavors "prod dev"`. + +The generic entrypoint dispatches to `os//build.sh`. A future +backend such as mkosi can implement the same artifact-manifest contract without +changing the common assembler or release consumers. + +## Troubleshooting + +### A dependency directory is empty + +Run: + +```bash +make os-deps +git submodule status -- os/yocto/deps +``` + +Every listed dependency should start with a space, not `-`. + +### Docker permission is denied + +Ensure `docker version` works as the same non-root user that owns the +checkout. Do not run only part of the build as root; mixed ownership in +`build-a/` makes incremental builds difficult to repair. + +### A fetch task fails + +Yocto fetches many upstream sources. Preserve `build-a/`, confirm outbound +network and DNS access, then rerun `make os-image`; completed downloads and +tasks are reused. + +### `docker-compose do_fetch` repeatedly shows 0–100% + +This is not one archive being downloaded in a loop. Docker Compose has hundreds +of independently checksummed Go-module sources, while BitBake's terminal +percentage describes only the current source URL. The percentage therefore +returns to zero for every module even though the task timer and PID stay the +same. + +Let the first fetch finish. If it is interrupted, rerun the same command; +completed files have `.done` markers in the build directory's `downloads/` +cache and are not downloaded again. To confirm which URL is currently being +fetched during a native `make os` build, inspect the latest task log: + +```bash +find os/yocto/bb-build/tmp-mc-* -path '*docker-compose/*/temp/log.do_fetch' \ + -print -exec tail -n 5 {} \; +``` + +### The disk fills up + +The largest disposable directories are: + +```text +os/yocto/repro-build/build-a/ +os/yocto/repro-build/build-b/ +os/yocto/bb-build/ +``` + +They are ignored by Git and can be removed when no build is running. Keep +`dist/` separately if you need the release archives. + +### `reproduce.sh` is missing + +The image archives are still valid. The wrapper intentionally skips generating +`reproduce.sh` when `git status --porcelain` reports a dirty source tree, +because that script can reproduce only committed source revisions. diff --git a/docs/deployment.md b/docs/deployment.md index 532ec3f87..be439e30a 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -59,10 +59,10 @@ Clone and build dstack-vmm: ```bash git clone https://github.com/Dstack-TEE/dstack cd dstack -cargo build --release -p dstack-vmm -p supervisor +cargo build --manifest-path dstack/Cargo.toml --release -p dstack-vmm -p supervisor mkdir -p vmm-data -cp target/release/dstack-vmm vmm-data/ -cp target/release/supervisor vmm-data/ +cp dstack/target/release/dstack-vmm vmm-data/ +cp dstack/target/release/supervisor vmm-data/ cd vmm-data/ ``` @@ -93,7 +93,7 @@ address = "vsock:2" port = 10000 ``` -Download guest images from [meta-dstack releases](https://github.com/Dstack-TEE/meta-dstack/releases) and extract to `./images/`. +Download guest images from [dstack guest-OS releases](https://github.com/Dstack-TEE/dstack/releases) and extract to `./images/`. > For reproducible builds and verification, see the [Security Model](./security/security-model.md). @@ -115,8 +115,8 @@ Production KMS requires: | Server | Use Case | Configuration | |--------|----------|---------------| -| [auth-simple](../kms/auth-simple/) | Config-file-based whitelisting | JSON config file | -| [auth-eth](../kms/auth-eth/) | On-chain governance via smart contracts | Ethereum RPC + contract | +| [auth-simple](../dstack/kms/auth-simple/) | Config-file-based whitelisting | JSON config file | +| [auth-eth](../dstack/kms/auth-eth/) | On-chain governance via smart contracts | Ethereum RPC + contract | | Custom | Your own authorization logic | Implement webhook interface | All auth servers implement the same webhook interface: @@ -146,7 +146,7 @@ Create `auth-config.json` for initial KMS deployment: Run auth-simple: ```bash -cd kms/auth-simple +cd dstack/kms/auth-simple bun install PORT=3001 AUTH_CONFIG_PATH=/path/to/auth-config.json bun run start ``` @@ -206,7 +206,7 @@ Then run: **Monitor startup:** ```bash -tail -f ../../vmm-data/run/vm//serial.log +tail -f ../../../vmm-data/run/vm//serial.log ``` Wait for `[ OK ] Finished App Compose Service.` @@ -345,8 +345,9 @@ This will: Monitor for unauthorized certificates issued to your domain. ```bash -cargo build --release -p ct_monitor -./target/release/ct_monitor \ +cd /path/to/dstack +cargo build --manifest-path dstack/Cargo.toml --release -p ct_monitor +./dstack/target/release/ct_monitor \ --gateway-uri https:// \ --domain ``` diff --git a/docs/design-and-hardening-decisions.md b/docs/design-and-hardening-decisions.md index 81ae51ca7..af5dd0ae6 100644 --- a/docs/design-and-hardening-decisions.md +++ b/docs/design-and-hardening-decisions.md @@ -1,8 +1,8 @@ -# Design and Hardening Decisions in meta-dstack Layer +# Design and Hardening Decisions in the dstack Yocto Layer ## Overview -The meta-dstack layer is designed to create a minimally secure image for booting Confidential Virtual Machines (CVMs). Our design philosophy prioritizes attack surface reduction while maintaining TDX-aware functionality. This document outlines the architectural decisions and trade-offs made during development. +The dstack-owned Yocto layer under `os/yocto/layers/meta-dstack/` is designed to create a minimally secure image for booting Confidential Virtual Machines (CVMs). Our design philosophy prioritizes attack surface reduction while maintaining TDX-aware functionality. This document outlines the architectural decisions and trade-offs made during development. ## Key Design Decisions @@ -49,7 +49,7 @@ See [here](https://intel.github.io/ccc-linux-guest-hardening-docs/security-spec. ### 5. Secure System Time -**Implementation**: dstack OS enforces the guest kernel uses TSC as the only timer source by appending `tsc=reliable no-kvmclock` to the kernel cmdline. It also enforces the use of NTS with built-in [trusted servers](https://github.com/Dstack-TEE/meta-dstack/blob/bef2dfa850f4116ae4ece96d8c0948965c5874b3/meta-dstack/recipes-core/chrony/files/chrony.conf#L13-L20) to synchronize system time. +**Implementation**: dstack OS enforces the guest kernel uses TSC as the only timer source by appending `tsc=reliable no-kvmclock` to the kernel cmdline. It also enforces the use of NTS with built-in [trusted servers](../os/yocto/layers/meta-dstack/recipes-core/chrony/files/chrony.conf) to synchronize system time. **Behavior**: When `secure_time` is enabled in the app-compose.json configuration, the system ensures time synchronization is completed before requesting application keys. If `secure_time` is disabled, time synchronization is not enforced before application launch. diff --git a/docs/faq.md b/docs/faq.md index 80a6bc0ed..4f43b053a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -30,4 +30,4 @@ Once you have the required privileges, make sure the supervisor process is shut ps aux | grep supervisor | grep $(whoami) | grep -v grep ``` -Log out of all your sessions and log back in. Check your groups with the `id` command, and this should resolve the issue. \ No newline at end of file +Log out of all your sessions and log back in. Check your groups with the `id` command, and this should resolve the issue. diff --git a/docs/hardware-enablement.md b/docs/hardware-enablement.md index 46d6e7e76..fb5030d47 100644 --- a/docs/hardware-enablement.md +++ b/docs/hardware-enablement.md @@ -33,6 +33,9 @@ Do not install a generic QEMU package as a substitute for TDX host setup. Use th Use your vendor or distribution's SEV-SNP enablement path. The [AMDSEV project](https://github.com/AMDESE/AMDSEV) documents CPU, BIOS, firmware, kernel, QEMU, OVMF, and verification requirements for SEV-SNP hosts. Confidential Containers also keeps platform setup separate from its [quickstart](https://github.com/confidential-containers/documentation/blob/main/quickstart.md) and points SEV users to AMD host preparation from its [SEV guide](https://github.com/confidential-containers/documentation/blob/main/guides/sev.md). +After the host is enabled, follow [AMD SEV-SNP Support](./amd-sev-snp.md) for +the dstack guest image, installation, attestation, and KMS release policy. + For dstack, the host must have: - AMD SEV-SNP enabled in firmware and the host OS. diff --git a/docs/normalized-app-compose.md b/docs/normalized-app-compose.md index e6a99c83d..66ccb4c43 100644 --- a/docs/normalized-app-compose.md +++ b/docs/normalized-app-compose.md @@ -107,7 +107,7 @@ func main() { return } fmt.Println("Deterministic JSON:", string(jsonBytes)) - + // This JSON string can now be used to generate a compose hash } ``` @@ -243,4 +243,3 @@ Here's how each language handles deterministic JSON serialization for compose ha Getting deterministic JSON serialization across different languages for compose hash generation isn't the default behavior. It needs careful setup. Go works well with compact output and special value handling, but needs custom key sorting for maps. Python and JavaScript both need explicit setup for key sorting and compact output. JavaScript notably requires manual recursive sorting of object keys. By following these recommendations, dstack can ensure that the same `app-compose.json` file produces the same SHA256 compose hash across all its Go, Python, and JavaScript components. This provides a reliable foundation for the project's distributed architecture and change detection system. - diff --git a/docs/onboarding.md b/docs/onboarding.md index 169ed57ae..48df63c11 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -3,7 +3,7 @@ Use this guide to get a first dstack app running on one Intel TDX host. The workflow uses `dstackup` for host setup and `dstack` for app deployment: ```bash -curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/scripts/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/dstack/scripts/install.sh | sh sudo dstackup install sudo dstack deploy \ -n hello-nginx \ @@ -12,7 +12,14 @@ sudo dstack deploy \ curl http://127.0.0.1:8080/ ``` -AMD SEV-SNP hosts use the same `dstackup` and `dstack` commands after you provide a guest image that contains the image digest (`digest.txt`). +AMD SEV-SNP hosts use the same `dstackup` and `dstack` commands after you +provide a guest image that contains the image digest (`digest.txt`); see +[AMD SEV-SNP Support](./amd-sev-snp.md) for the experimental platform's image, +attestation, and KMS requirements. + +The default onboarding flow uses a published image. To build or customize the +guest OS first, follow [Build the dstack guest OS](./building-guest-os.md), then +install the resulting `dstack-.tar.gz` bundle. For multi-node production, Gateway TLS, custom domains, or on-chain governance, use the full [deployment guide](./deployment.md). @@ -71,7 +78,7 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y Build and install the `dstackup` bootstrap command: ```bash -curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/scripts/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/dstack/scripts/install.sh | sh ``` The bootstrap installer builds `dstackup` from a temporary source checkout and installs it under `/usr/local/bin`. The `dstackup install` command then builds and installs `dstack`, `dstack-auth`, `dstack-vmm`, `supervisor`, static assets, and host config into the system layout. @@ -84,7 +91,7 @@ Run: sudo dstackup install ``` -`dstackup install` auto-detects TDX or AMD SEV-SNP. If no local guest image exists, it downloads the latest CPU image from [meta-dstack releases](https://github.com/Dstack-TEE/meta-dstack/releases), requires the release SHA-256 digest by default, verifies the tarball, stages the unpack, and only then adopts the image. +`dstackup install` auto-detects TDX or AMD SEV-SNP. If no local guest image exists, it downloads the latest unified image from [dstack guest-OS releases](https://github.com/Dstack-TEE/dstack/releases?q=guest-os-v), requires the release SHA-256 digest by default, verifies the tarball, stages the unpack, and only then adopts the image. Current images include NVIDIA support conditionally and work on CPU-only hosts too. Pinned versions below 0.6.0 are read directly from the archived [`meta-dstack` releases](https://github.com/Dstack-TEE/meta-dstack/releases); versions 0.6.0 and later come from this repository. On TDX, `dstackup install` starts the SGX key provider automatically from `/usr/local/share/dstack/key-provider-build`. To use a different provider, pass one of: @@ -95,14 +102,16 @@ sudo dstackup install --use-existing-key-provider 127.0.0.1:3443 On AMD SEV-SNP, no SGX key provider is needed. The selected guest image must include `digest.txt`; otherwise, `dstackup install` fails before it starts the host units because apps could not be pinned to the measured OS image. -To use a GPU image, pull it before install: +The normal pull command is sufficient for current CPU and GPU hosts: ```bash -sudo dstackup image pull --gpu +sudo dstackup image pull sudo dstackup install ``` -If multiple images are present, pass the image name or release version to `--image`, such as `dstack-0.5.11`, `dstack-nvidia-0.5.11`, or `0.5.11`. If the requested release-shaped image is not local, `dstackup install` downloads it. +`dstackup image pull --gpu` remains a compatibility option for older releases that published a separate `dstack-nvidia-*` archive; it falls back to the unified archive when a release has no separate GPU asset. + +If multiple images are present, pass the image name or release version to `--image`, such as `dstack-0.6.0`, legacy `dstack-nvidia-0.5.11`, or `0.6.0`. If the requested release-shaped image is not local, `dstackup install` downloads it. When install succeeds, it prints the dashboard URL, the KMS address, and a `dstack deploy` command template. The default dashboard URL is: @@ -207,7 +216,7 @@ Use `--prefix` when you want a second isolated install on the same host. A custo Install `dstackup` into the prefix, then use the same prefix for `dstackup` and `dstack`: ```bash -curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/scripts/install.sh | sh -s -- --prefix /opt/dstack-test +curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/dstack/scripts/install.sh | sh -s -- --prefix /opt/dstack-test sudo /opt/dstack-test/bin/dstackup install \ --prefix /opt/dstack-test \ @@ -246,7 +255,7 @@ Use the [deployment guide](./deployment.md) when you need domain routing, Gatewa ### Image download fails -`dstackup install` downloads the latest CPU image when KMS mode needs an image and none exists locally. If the download fails, check network access to GitHub and the meta-dstack release: +`dstackup install` downloads the latest CPU image when KMS mode needs an image and none exists locally. If the download fails, check network access to GitHub and the dstack guest-OS release: ```bash sudo dstackup image pull diff --git a/docs/onchain-governance.md b/docs/onchain-governance.md index a78598a65..42099c069 100644 --- a/docs/onchain-governance.md +++ b/docs/onchain-governance.md @@ -193,7 +193,7 @@ struct AppBootInfo { } ``` -Source: [`kms/auth-eth/contracts/`](../kms/auth-eth/contracts/) +Source: [`dstack/kms/auth-eth/contracts/`](../dstack/kms/auth-eth/contracts/) ## See Also diff --git a/docs/security/cvm-boundaries.md b/docs/security/cvm-boundaries.md index e61103d9c..5128a07ac 100644 --- a/docs/security/cvm-boundaries.md +++ b/docs/security/cvm-boundaries.md @@ -152,7 +152,7 @@ The dstack-guest-agent listens on VSOCK port 8000 inside the CVM, providing inte | ListContainers | List running containers | ListContainersResponse | | Shutdown | Gracefully shut down the guest | Empty | -Full specification: [guest_api.proto](../../guest-api/proto/guest_api.proto) +Full specification: [guest_api.proto](../../dstack/guest-api/proto/guest_api.proto) ### VSOCK-based Host API Service @@ -170,7 +170,7 @@ The dstack-vmm listens on a configured VSOCK port on the bare-metal host system. | Notify | Send notification to host | Notification | Empty | | GetSealingKey | Retrieve sealing key | GetSealingKeyRequest | GetSealingKeyResponse | -Full specification: [host_api.proto](../../host-api/proto/host_api.proto) +Full specification: [host_api.proto](../../dstack/host-api/proto/host_api.proto) ### HTTP-based Public Guest API Service @@ -187,6 +187,6 @@ The dstack-guest-agent runs an HTTP server on port 8090 inside the CVM. This por | Info | Get application information | AppInfo | | Version | Get guest agent version | WorkerVersion | -The service also provides a web dashboard at the root URL (`/`) showing basic CVM information. View the dashboard template [here](../../guest-agent/templates/dashboard.html). +The service also provides a web dashboard at the root URL (`/`) showing basic CVM information. View the dashboard template [here](../../dstack/guest-agent/templates/dashboard.html). -Full specification: [agent_rpc.proto](../../guest-agent/rpc/proto/agent_rpc.proto) +Full specification: [agent_rpc.proto](../../dstack/guest-agent/rpc/proto/agent_rpc.proto) diff --git a/docs/security/security-model.md b/docs/security/security-model.md index 4cbee519c..8a9aef69a 100644 --- a/docs/security/security-model.md +++ b/docs/security/security-model.md @@ -12,7 +12,7 @@ The only thing you must trust is **TEE hardware**. Intel TDX is the production p Everything else is verifiable. -**The dstack OS** is measured during boot and recorded in the attestation quote. You verify it by rebuilding from [meta-dstack](https://github.com/Dstack-TEE/meta-dstack) source and comparing measurements, or by checking that the OS hash is whitelisted in a governance contract you trust. +**The dstack OS** is measured during boot and recorded in the attestation quote. You verify it by rebuilding from the [`os/`](../../os/) source and comparing measurements, or by checking that the OS hash is whitelisted in a governance contract you trust. **The KMS** runs in its own TEE with its own attestation quote. You verify it the same way you verify any dstack workload. @@ -86,7 +86,7 @@ dstack implements layered verification from hardware to application. Each layer **Hardware layer.** The TEE provides the root of trust. The attestation quote is cryptographically signed by TEE hardware, and verification confirms the signature chain. The TCB status shows whether firmware is patched against known vulnerabilities. -**OS layer.** The dstack OS is measured during boot into MRTD and RTMR0-2. MRTD captures the virtual firmware. RTMR0 captures firmware configuration. RTMR1 captures the Linux kernel. RTMR2 captures kernel command-line parameters. You verify integrity by computing expected measurements from meta-dstack source and comparing them to the quote. +**OS layer.** The dstack OS is measured during boot into MRTD and RTMR0-2. MRTD captures the virtual firmware. RTMR0 captures firmware configuration. RTMR1 captures the Linux kernel. RTMR2 captures kernel command-line parameters. You verify integrity by computing expected measurements from the monorepo OS source and comparing them to the quote. **Application layer.** Your application is measured into RTMR3 as the compose-hash, which is the SHA256 hash of your normalized docker-compose configuration. Each image must use SHA256 digest pinning. This proves exactly which container images are running and that no code substitution happened after measurement. diff --git a/docs/tutorials/attestation-verification.md b/docs/tutorials/attestation-verification.md index 972f70a01..aec14d5b5 100644 --- a/docs/tutorials/attestation-verification.md +++ b/docs/tutorials/attestation-verification.md @@ -114,7 +114,7 @@ Before starting, ensure you have: Verify you have a running CVM: ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm ``` @@ -126,7 +126,7 @@ The VMM provides a `/guest/Info` endpoint that proxies into the CVM and retrieve ### Via VMM Guest Proxy ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) # Get the VM UUID for hello-world @@ -274,7 +274,7 @@ rm -rf qemu-tdx ### Build the measurement calculator ```bash -cd ~/dstack +cd ~/dstack/dstack cargo build --release -p dstack-mr-cli ``` @@ -369,7 +369,7 @@ Compare the CVM's actual measurements against your expected values: #!/bin/bash # verify-measurements.sh -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) # Get VM UUID @@ -552,7 +552,7 @@ echo "Instance: $INSTANCE_NAME" echo "Image: $IMAGE_VERSION" echo "" -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) # --- Step 1: Get VM UUID --- @@ -662,7 +662,7 @@ if [ -x "$DSTACK_MR" ] && [ -f "$METADATA" ]; then done else echo " SKIP - dstack-mr not built or metadata not found" - echo " To enable: cd ~/dstack && cargo build --release -p dstack-mr-cli" + echo " To enable: cd ~/dstack/dstack && cargo build --release -p dstack-mr-cli" fi # --- Step 6: Display event log --- @@ -785,8 +785,18 @@ dstack-mr measure --cpu 2 --memory 2G \ For highest assurance, build images from source: ```bash -git clone https://github.com/Dstack-TEE/meta-dstack.git -cd meta-dstack/repro-build +git clone https://github.com/Dstack-TEE/dstack.git +cd dstack +git submodule update --init -- \ + os/yocto/deps/bitbake \ + os/yocto/deps/openembedded-core \ + os/yocto/deps/meta-yocto \ + os/yocto/deps/meta-confidential-compute \ + os/yocto/deps/meta-virtualization \ + os/yocto/deps/meta-openembedded \ + os/yocto/deps/meta-rust-bin \ + os/yocto/deps/meta-security +cd os/yocto/repro-build ./repro-build.sh -n # Reproducible build ``` @@ -845,4 +855,4 @@ With the foundation complete, you're ready to explore: - [Intel TDX Documentation](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html) - [DCAP Attestation Guide](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/) - [dstack Attestation Source](https://github.com/Dstack-TEE/dstack/tree/main/attestation) -- [Reproducible Builds for meta-dstack](https://github.com/Dstack-TEE/meta-dstack/tree/main/repro-build) +- [Reproducible guest-OS builds](../../os/yocto/repro-build/) diff --git a/docs/tutorials/clone-build-dstack-vmm.md b/docs/tutorials/clone-build-dstack-vmm.md index 1e731e8e4..c1d15f2b8 100644 --- a/docs/tutorials/clone-build-dstack-vmm.md +++ b/docs/tutorials/clone-build-dstack-vmm.md @@ -55,25 +55,27 @@ All build commands should be run as the `ubuntu` user. Only the final installati ### Step 2: Verify dstack Repository -The dstack repository should already be cloned and checked out at v0.5.7 from [Gramine Key Provider](/tutorial/gramine-key-provider): +The dstack repository should already be cloned and checked out on the current +`master` branch from [Gramine Key Provider](/tutorial/gramine-key-provider): ```bash cd ~/dstack git describe --tags -# Should show v0.5.7 +git branch --show-current +# Should show master ``` ### Step 3: Build dstack-vmm ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm cargo build --release ``` ### Step 5: Build dstack-supervisor ```bash -cd ~/dstack +cd ~/dstack/dstack cargo build --release -p supervisor ``` @@ -81,11 +83,11 @@ cargo build --release -p supervisor ```bash # Install VMM -sudo cp ~/dstack/target/release/dstack-vmm /usr/local/bin/dstack-vmm +sudo cp ~/dstack/dstack/target/release/dstack-vmm /usr/local/bin/dstack-vmm sudo chmod 755 /usr/local/bin/dstack-vmm # Install supervisor -sudo cp ~/dstack/target/release/supervisor /usr/local/bin/dstack-supervisor +sudo cp ~/dstack/dstack/target/release/supervisor /usr/local/bin/dstack-supervisor sudo chmod 755 /usr/local/bin/dstack-supervisor ``` @@ -106,12 +108,12 @@ ls -la /usr/local/bin/dstack-supervisor ### Specify a Different Version ```bash -# Check out a specific version -git checkout v0.5.4 +# Check out a monorepo-era release tag when one is available +git checkout -# Or use main branch for latest development -git checkout main -git pull +# Or use the master branch for latest development +git checkout master +git pull --ff-only ``` ### Clean Build @@ -119,6 +121,7 @@ git pull To rebuild from scratch: ```bash +cd ~/dstack/dstack cargo clean cargo build --release ``` @@ -128,8 +131,9 @@ cargo build --release For development with better error messages: ```bash +cd ~/dstack/dstack cargo build -# Binary at ~/dstack/target/debug/dstack-vmm +# Binary at ~/dstack/dstack/target/debug/dstack-vmm ``` --- diff --git a/docs/tutorials/contract-deployment.md b/docs/tutorials/contract-deployment.md index 3c944f14c..e01085edb 100644 --- a/docs/tutorials/contract-deployment.md +++ b/docs/tutorials/contract-deployment.md @@ -29,7 +29,7 @@ Before starting, ensure you have: - Completed [Blockchain Wallet Setup](/tutorial/blockchain-setup) with: - Wallet private key stored in `~/.dstack/secrets/sepolia-private-key` - Sepolia testnet ETH (~0.01 ETH recommended) -- dstack repository cloned locally at v0.5.7: `git clone -b v0.5.7 https://github.com/Dstack-TEE/dstack ~/dstack` +- Current dstack monorepo cloned locally: `git clone https://github.com/Dstack-TEE/dstack ~/dstack` ## What Gets Deployed The deployment creates two smart contracts on Sepolia: @@ -45,17 +45,17 @@ These contracts use the UUPS (Universal Upgradeable Proxy Standard) pattern for ## Deployment -> **Important: Run these steps on your LOCAL machine, not on the TDX server.** Contract deployment requires your Ethereum private key. By running locally, your private key never touches the server. You need a clone of the dstack repo on your local machine: `git clone -b v0.5.7 https://github.com/Dstack-TEE/dstack ~/dstack` +> **Important: Run these steps on your LOCAL machine, not on the TDX server.** Contract deployment requires your Ethereum private key. By running locally, your private key never touches the server. You need a current clone of the dstack repo on your local machine: `git clone https://github.com/Dstack-TEE/dstack ~/dstack` ### Step 1: Clone Repository and Navigate to auth-eth -On your **local machine**, clone the dstack repository (if you haven't already) and check out v0.5.7: +On your **local machine**, clone the dstack repository (if you haven't already) and use the current `master` branch: ```bash git clone https://github.com/Dstack-TEE/dstack.git ~/dstack 2>/dev/null || true cd ~/dstack -git checkout v0.5.7 -cd kms/auth-eth +git checkout master +cd dstack/kms/auth-eth ``` ### Step 2: Install Node.js and Dependencies diff --git a/docs/tutorials/gateway-build-configuration.md b/docs/tutorials/gateway-build-configuration.md index 0d24e8156..3a807f8df 100644 --- a/docs/tutorials/gateway-build-configuration.md +++ b/docs/tutorials/gateway-build-configuration.md @@ -115,19 +115,19 @@ The `dstacktee/dstack-gateway:0.5.7` image isn't published on Docker Hub, so we The [Build dstack from Source](/tutorial/clone-build-dstack-vmm) tutorial builds `dstack-vmm` and `supervisor`, but not the gateway. Build it now: ```bash -cd ~/dstack +cd ~/dstack/dstack cargo build --release -p dstack-gateway ``` Verify the binary was built: ```bash -ls -lh ~/dstack/target/release/dstack-gateway +ls -lh ~/dstack/dstack/target/release/dstack-gateway ``` Expected output (typically 15-25MB): ``` --rwxrwxr-x 1 ubuntu ubuntu 20M ... /home/ubuntu/dstack/target/release/dstack-gateway +-rwxrwxr-x 1 ubuntu ubuntu 20M ... /home/ubuntu/dstack/dstack/target/release/dstack-gateway ``` #### Create Dockerfile @@ -161,8 +161,8 @@ EOF Copy the gateway binary and entrypoint script into the build context: ```bash -cp ~/dstack/target/release/dstack-gateway ~/gateway-deploy/ -cp ~/dstack/gateway/dstack-app/builder/entrypoint.sh ~/gateway-deploy/ +cp ~/dstack/dstack/target/release/dstack-gateway ~/gateway-deploy/ +cp ~/dstack/dstack/gateway/dstack-app/builder/entrypoint.sh ~/gateway-deploy/ ``` #### Build Docker image @@ -458,7 +458,7 @@ cat .app_env Now generate the VMM deployment manifest: ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) diff --git a/docs/tutorials/gateway-service-setup.md b/docs/tutorials/gateway-service-setup.md index 2418d09fe..ba693486e 100644 --- a/docs/tutorials/gateway-service-setup.md +++ b/docs/tutorials/gateway-service-setup.md @@ -84,7 +84,7 @@ Load environment variables and deploy: cd ~/gateway-deploy set -a; source .env; set +a -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ./src/vmm-cli.py --url http://127.0.0.1:9080 deploy \ @@ -369,7 +369,7 @@ Expected output shows both domains with `has_cert: true` and expiry dates ~90 da If the cert status shows `has_cert: false`, check the gateway logs for ACME errors: ```bash -VM_ID=$(cd ~/dstack/vmm && ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm --json | jq -r '.[] | select(.name=="dstack-gateway") | .id') +VM_ID=$(cd ~/dstack/dstack/vmm && ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm --json | jq -r '.[] | select(.name=="dstack-gateway") | .id') curl -s -H "Authorization: Bearer $(cat ~/.dstack/secrets/vmm-auth-token)" \ "http://127.0.0.1:9080/logs?id=$VM_ID&follow=false&ansi=false&lines=50" | grep -i "cert\|renew\|acme" ``` @@ -407,7 +407,7 @@ sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy Navigate to the VMM directory first: ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ``` diff --git a/docs/tutorials/gramine-key-provider.md b/docs/tutorials/gramine-key-provider.md index 89f2547bc..1a7cf59af 100644 --- a/docs/tutorials/gramine-key-provider.md +++ b/docs/tutorials/gramine-key-provider.md @@ -105,13 +105,13 @@ Clone the dstack repository and check out the v0.5.7 release: cd ~ git clone https://github.com/Dstack-TEE/dstack.git cd dstack -git checkout v0.5.7 +git checkout master ``` ### Step 2: Navigate to Key Provider ```bash -cd ~/dstack/key-provider-build +cd ~/dstack/dstack/key-provider-build ls -la ``` @@ -125,7 +125,7 @@ You should see: The key provider needs to know where to find a PCCS for quote verification. Create the QCNL configuration file: ```bash -cat > ~/dstack/key-provider-build/sgx_default_qcnl.conf << 'EOF' +cat > ~/dstack/dstack/key-provider-build/sgx_default_qcnl.conf << 'EOF' { "pccs_url": "https://pccs.phala.network/sgx/certification/v4/", "use_secure_cert": false, @@ -143,7 +143,7 @@ The default configuration binds to localhost, but CVMs need to access the key pr ```bash # Change from 127.0.0.1:3443 to 0.0.0.0:3443 -sed -i 's/"127\.0\.0\.1:3443:3443"/"0.0.0.0:3443:3443"/' ~/dstack/key-provider-build/docker-compose.yaml +sed -i 's/"127\.0\.0\.1:3443:3443"/"0.0.0.0:3443:3443"/' ~/dstack/dstack/key-provider-build/docker-compose.yaml ``` > **Note:** This makes the key provider accessible from CVMs via the QEMU user-mode networking gateway (`10.0.2.2`). The key provider still verifies TDX quotes, so only legitimate CVMs can obtain keys. diff --git a/docs/tutorials/guest-image-setup.md b/docs/tutorials/guest-image-setup.md index e5ab2f39f..555b1ef1f 100644 --- a/docs/tutorials/guest-image-setup.md +++ b/docs/tutorials/guest-image-setup.md @@ -54,6 +54,10 @@ Before starting, ensure you have: If you prefer to set up guest images manually, follow these steps. +To produce the image from source instead of downloading a release, follow the +[guest-OS build guide](../building-guest-os.md). Use the generated +`os/yocto/repro-build/dist/dstack-.tar.gz` archive in Step 3 below. + ### Step 1: Create Image Directory Structure Create the directory where guest images will be stored: @@ -75,7 +79,13 @@ echo "Installing guest images for version: $DSTACK_VERSION" # Download the image archive cd /tmp -wget https://github.com/Dstack-TEE/meta-dstack/releases/download/v${DSTACK_VERSION}/dstack-${DSTACK_VERSION}.tar.gz +IFS=. read -r DSTACK_MAJOR DSTACK_MINOR _ <<< "$DSTACK_VERSION" +if (( DSTACK_MAJOR == 0 && DSTACK_MINOR < 6 )); then + IMAGE_URL="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/Dstack-TEE/meta-dstack/releases/download/v${DSTACK_VERSION}/dstack-${DSTACK_VERSION}.tar.gz" +else + IMAGE_URL="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/Dstack-TEE/dstack/releases/download/guest-os-v${DSTACK_VERSION}/dstack-${DSTACK_VERSION}.tar.gz" +fi +wget "$IMAGE_URL" ``` Verify the download: @@ -240,16 +250,16 @@ Use the `dstack-image-oci.sh` script to package and push a guest image directory ```bash # Push a standard image (auto-tags: version + sha256-hash) -./scripts/dstack-image-oci.sh push /var/lib/dstack/images/dstack-0.5.8 ghcr.io/your-org/guest-image +./os/image/dstack-image-oci.sh push /var/lib/dstack/images/dstack-0.6.0 ghcr.io/your-org/guest-image -# Push an nvidia variant -./scripts/dstack-image-oci.sh push /var/lib/dstack/images/dstack-nvidia-0.5.8 ghcr.io/your-org/guest-image +# Current unified image is also used on NVIDIA hosts +./os/image/dstack-image-oci.sh push /var/lib/dstack/images/dstack-0.6.0 ghcr.io/your-org/guest-image --tag 0.6.0 # Push with a custom tag -./scripts/dstack-image-oci.sh push /var/lib/dstack/images/dstack-0.5.8 ghcr.io/your-org/guest-image --tag latest +./os/image/dstack-image-oci.sh push /var/lib/dstack/images/dstack-0.6.0 ghcr.io/your-org/guest-image --tag latest # List tags in the registry -./scripts/dstack-image-oci.sh list ghcr.io/your-org/guest-image +./os/image/dstack-image-oci.sh list ghcr.io/your-org/guest-image ``` The script reads `metadata.json` and `digest.txt` from the image directory and auto-generates tags: @@ -384,7 +394,7 @@ With guest images configured and VMM able to access them, you're ready to deploy ## Additional Resources -- [meta-dstack Repository](https://github.com/Dstack-TEE/meta-dstack) +- [Guest OS source and build backends](../../os/) - [dstack GitHub Repository](https://github.com/Dstack-TEE/dstack) - [Yocto Project](https://www.yoctoproject.org/) - [TDX Guest Architecture](https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html) diff --git a/docs/tutorials/hello-world-app.md b/docs/tutorials/hello-world-app.md index 66dec5f29..2792e5ac7 100644 --- a/docs/tutorials/hello-world-app.md +++ b/docs/tutorials/hello-world-app.md @@ -183,7 +183,7 @@ ssh user@your-server Use `vmm-cli.py compose` to generate the encrypted deployment manifest. The `--gateway` and `--kms` flags enable gateway registration and KMS attestation. ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ./src/vmm-cli.py --url http://127.0.0.1:9080 compose \ @@ -264,7 +264,7 @@ ssh user@your-server ## Step 6: Deploy the CVM ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) SRV_DOMAIN=$(grep ^SRV_DOMAIN ~/gateway-deploy/.env | cut -d= -f2) @@ -372,7 +372,7 @@ This bypasses the gateway and hits nginx directly. You should see the same nginx Navigate to the VMM directory: ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ``` diff --git a/docs/tutorials/kms-build-configuration.md b/docs/tutorials/kms-build-configuration.md index e93e83692..c4ef875b2 100644 --- a/docs/tutorials/kms-build-configuration.md +++ b/docs/tutorials/kms-build-configuration.md @@ -67,7 +67,7 @@ Build the KMS service using Cargo in release mode. ### Navigate to repository root ```bash -cd ~/dstack +cd ~/dstack/dstack ``` ### Build KMS in release mode @@ -83,18 +83,18 @@ This compilation will: ### Verify the build ```bash -ls -lh ~/dstack/target/release/dstack-kms +ls -lh ~/dstack/dstack/target/release/dstack-kms ``` Expected output (typically 20-30MB): ``` --rwxrwxr-x 1 ubuntu ubuntu 25M Nov 20 10:30 /home/ubuntu/dstack/target/release/dstack-kms +-rwxrwxr-x 1 ubuntu ubuntu 25M Nov 20 10:30 /home/ubuntu/dstack/dstack/target/release/dstack-kms ``` ### Test the binary ```bash -~/dstack/target/release/dstack-kms --help +~/dstack/dstack/target/release/dstack-kms --help ``` This displays available command-line options. @@ -106,7 +106,7 @@ Install the KMS binary to a system-wide location. ### Copy to /usr/local/bin ```bash -sudo cp ~/dstack/target/release/dstack-kms /usr/local/bin/dstack-kms +sudo cp ~/dstack/dstack/target/release/dstack-kms /usr/local/bin/dstack-kms sudo chmod 755 /usr/local/bin/dstack-kms ``` @@ -254,7 +254,7 @@ You should see Node.js v20.x and npm v10.x (or later). ### Navigate to auth-eth directory ```bash -cd ~/dstack/kms/auth-eth +cd ~/dstack/dstack/kms/auth-eth ``` ### Install dependencies @@ -513,10 +513,10 @@ EOF ```bash # Copy KMS binary -cp ~/dstack/target/release/dstack-kms . +cp ~/dstack/dstack/target/release/dstack-kms . # Copy auth-eth service -cp -r ~/dstack/kms/auth-eth auth-eth +cp -r ~/dstack/dstack/kms/auth-eth auth-eth # Copy auth-eth environment config cp /etc/kms/auth-eth.env . diff --git a/docs/tutorials/kms-cvm-deployment.md b/docs/tutorials/kms-cvm-deployment.md index f86058c67..b54051ae4 100644 --- a/docs/tutorials/kms-cvm-deployment.md +++ b/docs/tutorials/kms-cvm-deployment.md @@ -253,7 +253,7 @@ Use the VMM CLI tool to deploy the CVM: ```bash # Navigate to dstack VMM directory -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm # Set VMM auth from saved token export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) diff --git a/docs/tutorials/troubleshooting-dstack-installation.md b/docs/tutorials/troubleshooting-dstack-installation.md index 2b5182ac7..c75198c1e 100644 --- a/docs/tutorials/troubleshooting-dstack-installation.md +++ b/docs/tutorials/troubleshooting-dstack-installation.md @@ -108,6 +108,7 @@ rustup update stable ### Network timeout downloading crates ```bash +cd ~/dstack/dstack export CARGO_HTTP_TIMEOUT=300 cargo build --release ``` @@ -124,16 +125,17 @@ sudo apt install -y build-essential pkg-config libssl-dev ```bash # Ensure you're using sudo -sudo cp ~/dstack/target/release/dstack-vmm /usr/local/bin/ +sudo cp ~/dstack/dstack/target/release/dstack-vmm /usr/local/bin/ # Or install to user directory mkdir -p ~/.local/bin -cp ~/dstack/target/release/dstack-vmm ~/.local/bin/ +cp ~/dstack/dstack/target/release/dstack-vmm ~/.local/bin/ ``` ### Build cache issues ```bash +cd ~/dstack/dstack cargo clean cargo update cargo build --release @@ -345,8 +347,14 @@ Try alternative download methods: ```bash # Using curl instead of wget +IFS=. read -r DSTACK_MAJOR DSTACK_MINOR _ <<< "$DSTACK_VERSION" +if (( DSTACK_MAJOR == 0 && DSTACK_MINOR < 6 )); then + IMAGE_URL="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/Dstack-TEE/meta-dstack/releases/download/v${DSTACK_VERSION}/dstack-${DSTACK_VERSION}.tar.gz" +else + IMAGE_URL="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/Dstack-TEE/dstack/releases/download/guest-os-v${DSTACK_VERSION}/dstack-${DSTACK_VERSION}.tar.gz" +fi curl -L -o dstack-${DSTACK_VERSION}.tar.gz \ - https://github.com/Dstack-TEE/meta-dstack/releases/download/v${DSTACK_VERSION}/dstack-${DSTACK_VERSION}.tar.gz + "$IMAGE_URL" ``` ### Image metadata missing diff --git a/docs/tutorials/troubleshooting-first-application.md b/docs/tutorials/troubleshooting-first-application.md index 1a5dd8f6f..ac5c11647 100644 --- a/docs/tutorials/troubleshooting-first-application.md +++ b/docs/tutorials/troubleshooting-first-application.md @@ -83,7 +83,7 @@ curl -s -H "Authorization: Bearer $(cat ~/.dstack/secrets/vmm-auth-token)" \ If `/guest/Info` returns empty or errors, check that the CVM is running: ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm ``` diff --git a/docs/tutorials/troubleshooting-gateway-deployment.md b/docs/tutorials/troubleshooting-gateway-deployment.md index 679ecfa51..1ef5846d1 100644 --- a/docs/tutorials/troubleshooting-gateway-deployment.md +++ b/docs/tutorials/troubleshooting-gateway-deployment.md @@ -71,7 +71,7 @@ export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) **Solution:** Redeploy the KMS CVM: ```bash -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) # Get KMS VM ID and remove it @@ -231,7 +231,7 @@ Let's Encrypt production allows **10 duplicate certificates per 3 hours per IP** **How to check if you're rate-limited:** ```bash -VM_ID=$(cd ~/dstack/vmm && ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm --json | jq -r '.[] | select(.name=="dstack-gateway") | .id') +VM_ID=$(cd ~/dstack/dstack/vmm && ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm --json | jq -r '.[] | select(.name=="dstack-gateway") | .id') curl -s -H "Authorization: Bearer $(cat ~/.dstack/secrets/vmm-auth-token)" \ "http://127.0.0.1:9080/logs?id=$VM_ID&follow=false&ansi=false&lines=200" | grep -i "rate\|too many\|acme.*error" ``` @@ -294,7 +294,7 @@ If KMS itself is not running: curl -sk https://localhost:9100/prpc/KMS.GetMeta | jq '{chain_id}' # Verify KMS CVM is running -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm ``` diff --git a/docs/tutorials/troubleshooting-kms-deployment.md b/docs/tutorials/troubleshooting-kms-deployment.md index f60b3bb02..7a4219ea0 100644 --- a/docs/tutorials/troubleshooting-kms-deployment.md +++ b/docs/tutorials/troubleshooting-kms-deployment.md @@ -230,7 +230,7 @@ Check CVM network configuration: cat ~/kms-deployment/docker-compose.yml | grep ports -A2 # Check CVM status via vmm-cli.py -cd ~/dstack/vmm +cd ~/dstack/dstack/vmm export DSTACK_VMM_AUTH_PASSWORD=$(cat ~/.dstack/secrets/vmm-auth-token) ./src/vmm-cli.py --url http://127.0.0.1:9080 lsvm ``` diff --git a/docs/tutorials/troubleshooting-prerequisites.md b/docs/tutorials/troubleshooting-prerequisites.md index 16e75294c..a4ebf93fd 100644 --- a/docs/tutorials/troubleshooting-prerequisites.md +++ b/docs/tutorials/troubleshooting-prerequisites.md @@ -289,7 +289,7 @@ docker logs aesmd 2>&1 | tail -30 **Solution:** 1. Verify QCNL configuration points to `https://pccs.phala.network/sgx/certification/v4/` 2. Check network connectivity: `curl -sk https://pccs.phala.network/sgx/certification/v4/rootcacrl` -3. Verify the QCNL config file exists at `~/dstack/key-provider-build/sgx_default_qcnl.conf` +3. Verify the QCNL config file exists at `~/dstack/dstack/key-provider-build/sgx_default_qcnl.conf` ### Empty response from curl test diff --git a/docs/verification.md b/docs/verification.md index 17c5170c2..e12af7a24 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -18,12 +18,16 @@ If any of these fail, the cryptographic proof won't verify. **Programmatic verification**: dstack provides several tools: -- [dstack-verifier](https://github.com/Dstack-TEE/dstack/tree/master/verifier) - HTTP service with `/verify` endpoint, also runs as CLI +- [dstack-verifier](https://github.com/Dstack-TEE/dstack/tree/master/dstack/verifier) - HTTP service with `/verify` endpoint, also runs as CLI - [dcap-qvl](https://github.com/Phala-Network/dcap-qvl) - Open source quote verification library (Rust, Python, JS/WASM, CLI) - [SDKs](../sdk/) - JavaScript and Python SDKs include `replayRtmrs()` for local RTMR verification ## Learn More +- [Intel TDX attestation](./attestation-tdx.md) - Verify TDX measurements and runtime events +- [AMD SEV-SNP support](./amd-sev-snp.md) - SNP image, attestation, and key-release requirements +- [GCP attestation](./attestation-gcp.md) - Verify the GCP TDX and TPM evidence chain +- [AWS Nitro Enclaves attestation](./attestation-nitro-enclave.md) - Verify NSM evidence - [Attestation Documentation](https://docs.phala.com/phala-cloud/attestation/overview) - Generating quotes, programmatic verification, RTMR3 replay - [Confidential AI Verification](https://docs.phala.com/phala-cloud/confidential-ai/verify/overview) - GPU TEE attestation for AI workloads - [Domain Attestation](https://docs.phala.com/phala-cloud/networking/domain-attestation) - TLS certificates managed in TEE diff --git a/Cargo.lock b/dstack/Cargo.lock similarity index 99% rename from Cargo.lock rename to dstack/Cargo.lock index f7ca74b03..c4af49cfd 100644 --- a/Cargo.lock +++ b/dstack/Cargo.lock @@ -2911,6 +2911,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "tempfile", "tokio", ] diff --git a/Cargo.toml b/dstack/Cargo.toml similarity index 98% rename from Cargo.toml rename to dstack/Cargo.toml index d5af4cf52..095218e9a 100644 --- a/Cargo.toml +++ b/dstack/Cargo.toml @@ -60,8 +60,8 @@ members = [ "verifier", "size-parser", "port-forward", - "sdk/rust", - "sdk/rust/types", + "../sdk/rust", + "../sdk/rust/types", "no_std_check", "crates/dstack-cli-core", "crates/dstack-cli", @@ -74,7 +74,7 @@ resolver = "2" # Internal dependencies ra-rpc = { path = "ra-rpc", default-features = false } ra-tls = { path = "ra-tls" } -dstack-sdk-types = { path = "sdk/rust/types", version = "0.1.3", default-features = false } +dstack-sdk-types = { path = "../sdk/rust/types", version = "0.1.3", default-features = false } dstack-gateway-rpc = { path = "gateway/rpc" } dstack-kms-rpc = { path = "kms/rpc" } dstack-guest-agent-rpc = { path = "guest-agent/rpc" } diff --git a/build/shared/build-lib.sh b/dstack/build/shared/build-lib.sh similarity index 95% rename from build/shared/build-lib.sh rename to dstack/build/shared/build-lib.sh index 0f4565031..d24d368d4 100755 --- a/build/shared/build-lib.sh +++ b/dstack/build/shared/build-lib.sh @@ -7,7 +7,7 @@ # Shared build library for reproducible Docker image builds. # # Expected variables (set by the sourcing script): -# REPO_ROOT - absolute path to the git repo root +# REPO_ROOT - absolute path to the monorepo root # CONTEXT_DIR - Docker build context directory # DOCKERFILE - path to the Dockerfile # GIT_REV - git revision to build @@ -17,7 +17,7 @@ set -euo pipefail BUILDKIT_VERSION="v0.20.2" BUILDKIT_BUILDER="buildkit_20" -BUILD_SHARED_DIR="$REPO_ROOT/build/shared" +BUILD_SHARED_DIR="$REPO_ROOT/dstack/build/shared" ensure_buildkit() { if ! docker buildx inspect "$BUILDKIT_BUILDER" &>/dev/null; then diff --git a/build/shared/config-qemu.sh b/dstack/build/shared/config-qemu.sh similarity index 100% rename from build/shared/config-qemu.sh rename to dstack/build/shared/config-qemu.sh diff --git a/build/shared/pin-packages.sh b/dstack/build/shared/pin-packages.sh similarity index 100% rename from build/shared/pin-packages.sh rename to dstack/build/shared/pin-packages.sh diff --git a/build/shared/verify-pinned-packages.sh b/dstack/build/shared/verify-pinned-packages.sh similarity index 100% rename from build/shared/verify-pinned-packages.sh rename to dstack/build/shared/verify-pinned-packages.sh diff --git a/cached-cell/Cargo.toml b/dstack/cached-cell/Cargo.toml similarity index 100% rename from cached-cell/Cargo.toml rename to dstack/cached-cell/Cargo.toml diff --git a/cached-cell/src/lib.rs b/dstack/cached-cell/src/lib.rs similarity index 100% rename from cached-cell/src/lib.rs rename to dstack/cached-cell/src/lib.rs diff --git a/cargo-check-all.sh b/dstack/cargo-check-all.sh similarity index 100% rename from cargo-check-all.sh rename to dstack/cargo-check-all.sh diff --git a/cc-eventlog/Cargo.toml b/dstack/cc-eventlog/Cargo.toml similarity index 100% rename from cc-eventlog/Cargo.toml rename to dstack/cc-eventlog/Cargo.toml diff --git a/cc-eventlog/samples/ccel.bin b/dstack/cc-eventlog/samples/ccel.bin similarity index 100% rename from cc-eventlog/samples/ccel.bin rename to dstack/cc-eventlog/samples/ccel.bin diff --git a/cc-eventlog/samples/tpm_eventlog.bin b/dstack/cc-eventlog/samples/tpm_eventlog.bin similarity index 100% rename from cc-eventlog/samples/tpm_eventlog.bin rename to dstack/cc-eventlog/samples/tpm_eventlog.bin diff --git a/cc-eventlog/src/codecs.rs b/dstack/cc-eventlog/src/codecs.rs similarity index 100% rename from cc-eventlog/src/codecs.rs rename to dstack/cc-eventlog/src/codecs.rs diff --git a/cc-eventlog/src/lib.rs b/dstack/cc-eventlog/src/lib.rs similarity index 100% rename from cc-eventlog/src/lib.rs rename to dstack/cc-eventlog/src/lib.rs diff --git a/cc-eventlog/src/runtime_events.rs b/dstack/cc-eventlog/src/runtime_events.rs similarity index 100% rename from cc-eventlog/src/runtime_events.rs rename to dstack/cc-eventlog/src/runtime_events.rs diff --git a/cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel-2.snap b/dstack/cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel-2.snap similarity index 100% rename from cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel-2.snap rename to dstack/cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel-2.snap diff --git a/cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel.snap b/dstack/cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel.snap similarity index 100% rename from cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel.snap rename to dstack/cc-eventlog/src/snapshots/cc_eventlog__tests__parse_ccel.snap diff --git a/cc-eventlog/src/tcg.rs b/dstack/cc-eventlog/src/tcg.rs similarity index 100% rename from cc-eventlog/src/tcg.rs rename to dstack/cc-eventlog/src/tcg.rs diff --git a/cc-eventlog/src/tdx.rs b/dstack/cc-eventlog/src/tdx.rs similarity index 100% rename from cc-eventlog/src/tdx.rs rename to dstack/cc-eventlog/src/tdx.rs diff --git a/cc-eventlog/src/tpm.rs b/dstack/cc-eventlog/src/tpm.rs similarity index 100% rename from cc-eventlog/src/tpm.rs rename to dstack/cc-eventlog/src/tpm.rs diff --git a/cert-client/Cargo.toml b/dstack/cert-client/Cargo.toml similarity index 100% rename from cert-client/Cargo.toml rename to dstack/cert-client/Cargo.toml diff --git a/cert-client/src/lib.rs b/dstack/cert-client/src/lib.rs similarity index 100% rename from cert-client/src/lib.rs rename to dstack/cert-client/src/lib.rs diff --git a/certbot/.gitignore b/dstack/certbot/.gitignore similarity index 100% rename from certbot/.gitignore rename to dstack/certbot/.gitignore diff --git a/certbot/Cargo.toml b/dstack/certbot/Cargo.toml similarity index 100% rename from certbot/Cargo.toml rename to dstack/certbot/Cargo.toml diff --git a/certbot/cli/Cargo.toml b/dstack/certbot/cli/Cargo.toml similarity index 100% rename from certbot/cli/Cargo.toml rename to dstack/certbot/cli/Cargo.toml diff --git a/certbot/cli/src/main.rs b/dstack/certbot/cli/src/main.rs similarity index 100% rename from certbot/cli/src/main.rs rename to dstack/certbot/cli/src/main.rs diff --git a/certbot/src/acme_client.rs b/dstack/certbot/src/acme_client.rs similarity index 100% rename from certbot/src/acme_client.rs rename to dstack/certbot/src/acme_client.rs diff --git a/certbot/src/acme_client/tests.rs b/dstack/certbot/src/acme_client/tests.rs similarity index 100% rename from certbot/src/acme_client/tests.rs rename to dstack/certbot/src/acme_client/tests.rs diff --git a/certbot/src/bot.rs b/dstack/certbot/src/bot.rs similarity index 100% rename from certbot/src/bot.rs rename to dstack/certbot/src/bot.rs diff --git a/certbot/src/bot/tests.rs b/dstack/certbot/src/bot/tests.rs similarity index 100% rename from certbot/src/bot/tests.rs rename to dstack/certbot/src/bot/tests.rs diff --git a/certbot/src/dns01_client.rs b/dstack/certbot/src/dns01_client.rs similarity index 100% rename from certbot/src/dns01_client.rs rename to dstack/certbot/src/dns01_client.rs diff --git a/certbot/src/dns01_client/cloudflare.rs b/dstack/certbot/src/dns01_client/cloudflare.rs similarity index 100% rename from certbot/src/dns01_client/cloudflare.rs rename to dstack/certbot/src/dns01_client/cloudflare.rs diff --git a/certbot/src/http_client.rs b/dstack/certbot/src/http_client.rs similarity index 100% rename from certbot/src/http_client.rs rename to dstack/certbot/src/http_client.rs diff --git a/certbot/src/lib.rs b/dstack/certbot/src/lib.rs similarity index 100% rename from certbot/src/lib.rs rename to dstack/certbot/src/lib.rs diff --git a/certbot/src/workdir.rs b/dstack/certbot/src/workdir.rs similarity index 100% rename from certbot/src/workdir.rs rename to dstack/certbot/src/workdir.rs diff --git a/crates/dstack-auth/Cargo.toml b/dstack/crates/dstack-auth/Cargo.toml similarity index 100% rename from crates/dstack-auth/Cargo.toml rename to dstack/crates/dstack-auth/Cargo.toml diff --git a/crates/dstack-auth/src/main.rs b/dstack/crates/dstack-auth/src/main.rs similarity index 100% rename from crates/dstack-auth/src/main.rs rename to dstack/crates/dstack-auth/src/main.rs diff --git a/crates/dstack-cli-core/Cargo.toml b/dstack/crates/dstack-cli-core/Cargo.toml similarity index 100% rename from crates/dstack-cli-core/Cargo.toml rename to dstack/crates/dstack-cli-core/Cargo.toml diff --git a/crates/dstack-cli-core/src/compose.rs b/dstack/crates/dstack-cli-core/src/compose.rs similarity index 100% rename from crates/dstack-cli-core/src/compose.rs rename to dstack/crates/dstack-cli-core/src/compose.rs diff --git a/crates/dstack-cli-core/src/config.rs b/dstack/crates/dstack-cli-core/src/config.rs similarity index 100% rename from crates/dstack-cli-core/src/config.rs rename to dstack/crates/dstack-cli-core/src/config.rs diff --git a/crates/dstack-cli-core/src/fsutil.rs b/dstack/crates/dstack-cli-core/src/fsutil.rs similarity index 100% rename from crates/dstack-cli-core/src/fsutil.rs rename to dstack/crates/dstack-cli-core/src/fsutil.rs diff --git a/crates/dstack-cli-core/src/host.rs b/dstack/crates/dstack-cli-core/src/host.rs similarity index 100% rename from crates/dstack-cli-core/src/host.rs rename to dstack/crates/dstack-cli-core/src/host.rs diff --git a/crates/dstack-cli-core/src/layout.rs b/dstack/crates/dstack-cli-core/src/layout.rs similarity index 100% rename from crates/dstack-cli-core/src/layout.rs rename to dstack/crates/dstack-cli-core/src/layout.rs diff --git a/crates/dstack-cli-core/src/lib.rs b/dstack/crates/dstack-cli-core/src/lib.rs similarity index 100% rename from crates/dstack-cli-core/src/lib.rs rename to dstack/crates/dstack-cli-core/src/lib.rs diff --git a/crates/dstack-cli-core/src/ports.rs b/dstack/crates/dstack-cli-core/src/ports.rs similarity index 100% rename from crates/dstack-cli-core/src/ports.rs rename to dstack/crates/dstack-cli-core/src/ports.rs diff --git a/crates/dstack-cli-core/src/vmm.rs b/dstack/crates/dstack-cli-core/src/vmm.rs similarity index 100% rename from crates/dstack-cli-core/src/vmm.rs rename to dstack/crates/dstack-cli-core/src/vmm.rs diff --git a/crates/dstack-cli/Cargo.toml b/dstack/crates/dstack-cli/Cargo.toml similarity index 100% rename from crates/dstack-cli/Cargo.toml rename to dstack/crates/dstack-cli/Cargo.toml diff --git a/crates/dstack-cli/src/main.rs b/dstack/crates/dstack-cli/src/main.rs similarity index 100% rename from crates/dstack-cli/src/main.rs rename to dstack/crates/dstack-cli/src/main.rs diff --git a/crates/dstackup/Cargo.toml b/dstack/crates/dstackup/Cargo.toml similarity index 94% rename from crates/dstackup/Cargo.toml rename to dstack/crates/dstackup/Cargo.toml index cfc3a8124..465008c85 100644 --- a/crates/dstackup/Cargo.toml +++ b/dstack/crates/dstackup/Cargo.toml @@ -24,3 +24,6 @@ serde = { workspace = true, features = ["derive"] } serde_json.workspace = true sha2.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] } + +[dev-dependencies] +tempfile.workspace = true diff --git a/crates/dstackup/src/cid.rs b/dstack/crates/dstackup/src/cid.rs similarity index 100% rename from crates/dstackup/src/cid.rs rename to dstack/crates/dstackup/src/cid.rs diff --git a/crates/dstackup/src/cli.rs b/dstack/crates/dstackup/src/cli.rs similarity index 98% rename from crates/dstackup/src/cli.rs rename to dstack/crates/dstackup/src/cli.rs index 89e1c566b..4d1b74689 100644 --- a/crates/dstackup/src/cli.rs +++ b/dstack/crates/dstackup/src/cli.rs @@ -75,12 +75,12 @@ impl ImageLoc { /// `dstackup image` subcommands. #[derive(Subcommand)] pub(crate) enum ImageCmd { - /// Download a guest OS image from meta-dstack releases. + /// Download a guest OS image from dstack guest-OS releases. Pull { /// image version to fetch (default: the latest release). #[arg(long, value_name = "VERSION")] version: Option, - /// fetch the gpu (nvidia) image instead of the cpu one. + /// prefer a legacy gpu image; current unified images are already GPU-capable. #[arg(long)] gpu: bool, #[command(flatten)] diff --git a/crates/dstackup/src/destroy.rs b/dstack/crates/dstackup/src/destroy.rs similarity index 100% rename from crates/dstackup/src/destroy.rs rename to dstack/crates/dstackup/src/destroy.rs diff --git a/crates/dstackup/src/image.rs b/dstack/crates/dstackup/src/image.rs similarity index 77% rename from crates/dstackup/src/image.rs rename to dstack/crates/dstackup/src/image.rs index 60b0a7b75..1874d31b9 100644 --- a/crates/dstackup/src/image.rs +++ b/dstack/crates/dstackup/src/image.rs @@ -4,8 +4,9 @@ //! `dstackup image` — fetch, list, and remove guest OS images. //! -//! Images are published as release tarballs at `Dstack-TEE/meta-dstack`. There -//! are two variants — cpu (`dstack-`) and gpu (`dstack-nvidia-`). +//! Images are published as `guest-os-v*` release tarballs in the dstack +//! monorepo. Current releases use one hardware-adaptive `dstack-` image; +//! legacy releases may also contain `dstack-nvidia-` variants. //! `install` validates the selected image against `digest.txt`, the OS image //! hash used on all platforms. HTTP + checksum are native (reqwest is //! already linked via the prpc client; sha2 verifies inline); only `tar` is @@ -22,8 +23,12 @@ use std::io::Write; use std::path::Path; use std::time::SystemTime; -const REPO: &str = "Dstack-TEE/meta-dstack"; -pub(crate) const RELEASES_URL: &str = "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/Dstack-TEE/meta-dstack/releases"; +const REPO: &str = "Dstack-TEE/dstack"; +const LEGACY_REPO: &str = "Dstack-TEE/meta-dstack"; +const RELEASE_TAG_PREFIX: &str = "guest-os-v"; +pub(crate) const RELEASES_URL: &str = "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/Dstack-TEE/dstack/releases?q=guest-os-v"; +const LEGACY_RELEASES_URL: &str = "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/Dstack-TEE/meta-dstack/releases"; +const MONOREPO_GUEST_OS_MIN_VERSION: (u64, u64, u64) = (0, 6, 0); /// the single rule for where images live: `--image-path` if given, else the /// image directory from the install layout. `install` and every image subcommand resolve through @@ -86,7 +91,7 @@ pub(crate) async fn cmd_image(cmd: ImageCmd) -> Result<()> { } } -/// download a guest image from the latest (or a specific) meta-dstack release. +/// Download a guest image from the latest (or a specific) guest-OS release. pub(crate) async fn pull( version: Option<&str>, gpu: bool, @@ -96,28 +101,18 @@ pub(crate) async fn pull( ) -> Result { println!( "dstackup image pull — {} image", - if gpu { "gpu (nvidia)" } else { "cpu" } + if gpu { + "gpu-capable (legacy nvidia variant preferred)" + } else { + "unified" + } ); let release = fetch_release(version).await?; - let ver = release.tag_name.trim_start_matches('v'); - - // the unpacked dir is usually `dstack[-nvidia]-`; check that first so a - // repeat pull is a cheap no-op instead of re-fetching a few hundred MB. - let expected = format!("dstack-{}{ver}", if gpu { "nvidia-" } else { "" }); - if !force - && Path::new(image_dir) - .join(&expected) - .join("metadata.json") - .exists() - { - println!(" [ok] {expected} already present (use --force to re-download)"); - return Ok(expected); - } let asset = pick_asset(&release.assets, gpu).with_context(|| { format!( - "no {} image tarball in meta-dstack release {} (assets: {})", - if gpu { "gpu" } else { "cpu" }, + "no suitable {} image tarball in guest-OS release {} (assets: {})", + if gpu { "GPU-capable" } else { "unified" }, release.tag_name, release .assets @@ -135,6 +130,23 @@ pub(crate) async fn pull( asset.name ); } + + // Release archives use their filename stem as the top-level image + // directory. Select the asset before this check because --gpu may resolve + // to a legacy dstack-nvidia archive or to the current unified image. + let expected = asset + .name + .strip_suffix(".tar.gz") + .context("guest image asset must end in .tar.gz")?; + if !force + && Path::new(image_dir) + .join(expected) + .join("metadata.json") + .exists() + { + println!(" [ok] {expected} already present (use --force to re-download)"); + return Ok(expected.to_string()); + } println!(" [..] release {} -> {}", release.tag_name, asset.name); fs::create_dir_all(image_dir).with_context(|| format!("creating {image_dir}"))?; @@ -177,8 +189,8 @@ async fn stage_image( .await?; fs::create_dir_all(staging).with_context(|| format!("creating {}", staging.display()))?; extract(&tmp.to_string_lossy(), &staging.to_string_lossy())?; - // the unpacked dir name needn't match the asset name (e.g. a `-uki` asset), - // so find the dir that actually holds a metadata.json. + // Do not assume the unpacked directory name matches the release asset; + // adopt the directory that actually contains metadata.json. let inner = image_subdirs(&staging.to_string_lossy()) .into_iter() .find(|d| staging.join(d).join("metadata.json").exists()) @@ -564,8 +576,8 @@ pub(crate) fn no_image_message(image_dir: &str) -> String { format!( "no guest image found in {image_dir}\n\n\ download the latest with:\n \ - {pull} # cpu image\n \ - {pull} --gpu # gpu (nvidia) image\n\n\ + {pull} # current unified CPU/GPU image\n \ + {pull} --gpu # prefer a legacy nvidia-specific asset\n\n\ images are published at {RELEASES_URL}" ) } @@ -581,46 +593,135 @@ fn missing_named_image_message(image_dir: &str, name: &str) -> String { ) } -/// GET the latest (or a tagged) release JSON from the github api. +/// Get the latest (or a tagged) guest-OS release from the GitHub API. +/// +/// Versions before 0.6.0 were released from `meta-dstack`; 0.6.0 and later are +/// released from this monorepo. Do not probe the new repository first for old +/// versions: the version boundary is authoritative and avoids redundant or +/// misleading requests. async fn fetch_release(version: Option<&str>) -> Result { - let url = match version { - Some(v) => format!( - "https://api.github.com/repos/{REPO}/releases/tags/v{}", - v.trim_start_matches('v') - ), - None => format!("https://api.github.com/repos/{REPO}/releases/latest"), - }; - reqwest::Client::new() - .get(&url) + let client = reqwest::Client::new(); + if let Some(version) = version { + let (version, url, releases_url) = tagged_release_location(version)?; + return fetch_tagged_release(&client, &url, releases_url) + .await? + .with_context(|| { + format!("guest-OS version {version} was not found; check {releases_url}") + }); + } + + let list_url = format!("https://api.github.com/repos/{REPO}/releases?per_page=100"); + let releases: Vec = client + .get(&list_url) .header("user-agent", "dstackup") .header("accept", "application/vnd.github+json") .send() .await - .context("requesting the github release")? + .context("requesting dstack releases")? .error_for_status() - .with_context(|| { - format!("github release lookup failed; check the version exists at {RELEASES_URL}") - })? + .with_context(|| format!("github release lookup failed; check {RELEASES_URL}"))? .json() .await - .context("parsing github release json") + .context("parsing dstack release list")?; + if let Some(release) = releases + .into_iter() + .find(|release| release.tag_name.starts_with(RELEASE_TAG_PREFIX)) + { + return Ok(release); + } + + let legacy_url = format!("https://api.github.com/repos/{LEGACY_REPO}/releases/latest"); + fetch_tagged_release(&client, &legacy_url, LEGACY_RELEASES_URL) + .await? + .with_context(|| format!("no guest-OS release found; check {RELEASES_URL}")) } -/// pick the cpu or gpu image tarball from a release's assets, skipping `-dev` -/// builds. cpu = `dstack-...`, gpu = `dstack-nvidia-...`. +fn tagged_release_location(version: &str) -> Result<(String, String, &'static str)> { + let version = version + .trim_start_matches(RELEASE_TAG_PREFIX) + .trim_start_matches('v'); + let core = numeric_version_core(version)?; + let (repo, tag_prefix, releases_url) = if core < MONOREPO_GUEST_OS_MIN_VERSION { + (LEGACY_REPO, "v", LEGACY_RELEASES_URL) + } else { + (REPO, RELEASE_TAG_PREFIX, RELEASES_URL) + }; + Ok(( + version.to_string(), + format!("https://api.github.com/repos/{repo}/releases/tags/{tag_prefix}{version}"), + releases_url, + )) +} + +fn numeric_version_core(version: &str) -> Result<(u64, u64, u64)> { + let mut parts = version.split('.'); + let major = parts.next().unwrap_or_default(); + let minor = parts.next().unwrap_or_default(); + let patch = parts.next().unwrap_or_default(); + let patch = patch.split_once('-').map_or(patch, |(numeric, _)| numeric); + if major.is_empty() + || minor.is_empty() + || patch.is_empty() + || !major.chars().all(|c| c.is_ascii_digit()) + || !minor.chars().all(|c| c.is_ascii_digit()) + || !patch.chars().all(|c| c.is_ascii_digit()) + { + bail!("invalid guest-OS version {version:?}; expected MAJOR.MINOR.PATCH"); + } + Ok((major.parse()?, minor.parse()?, patch.parse()?)) +} + +async fn fetch_tagged_release( + client: &reqwest::Client, + url: &str, + releases_url: &str, +) -> Result> { + let response = client + .get(url) + .header("user-agent", "dstackup") + .header("accept", "application/vnd.github+json") + .send() + .await + .context("requesting the github release")?; + if response.status() == reqwest::StatusCode::NOT_FOUND { + return Ok(None); + } + Ok(Some( + response + .error_for_status() + .with_context(|| format!("github release lookup failed; check {releases_url}"))? + .json() + .await + .context("parsing github release json")?, + )) +} + +/// Pick a full bare-metal image tarball, never the `-uki` archive. For a GPU +/// request, prefer a legacy `dstack-nvidia-*` asset when present and otherwise +/// use the current unified image (which already contains conditional NVIDIA +/// support). fn pick_asset(assets: &[Asset], gpu: bool) -> Option<&Asset> { - assets.iter().find(|a| { + let matches = |a: &&Asset, want_legacy_gpu: bool| { let n = a.name.as_str(); - if !n.ends_with(".tar.gz") || n.contains("-dev") { + if !n.ends_with(".tar.gz") || n.ends_with("-uki.tar.gz") || n.contains("-dev") { return false; } let is_gpu = n.starts_with("dstack-nvidia-"); - if gpu { + if want_legacy_gpu { is_gpu } else { n.starts_with("dstack-") && !is_gpu } - }) + }; + + if gpu { + assets + .iter() + .find(|asset| matches(asset, true)) + .or_else(|| assets.iter().find(|asset| matches(asset, false))) + } else { + assets.iter().find(|asset| matches(asset, false)) + } } fn extract(tarball: &str, into: &str) -> Result<()> { @@ -708,15 +809,54 @@ mod tests { } #[test] - fn gpu_only_release_has_no_cpu_asset() { - let assets = vec![asset("dstack-nvidia-0.6.0.a2-uki.tar.gz")]; - assert!(pick_asset(&assets, false).is_none()); + fn gpu_pull_falls_back_to_unified_image() { + let assets = vec![ + asset("dstack-0.6.0-uki.tar.gz"), + asset("dstack-0.6.0.tar.gz"), + ]; assert_eq!( pick_asset(&assets, true).unwrap().name, - "dstack-nvidia-0.6.0.a2-uki.tar.gz" + "dstack-0.6.0.tar.gz" ); } + #[test] + fn uki_archive_is_never_selected_as_a_host_image() { + let assets = vec![asset("dstack-nvidia-0.6.0.a2-uki.tar.gz")]; + assert!(pick_asset(&assets, false).is_none()); + assert!(pick_asset(&assets, true).is_none()); + } + + #[test] + fn routes_pinned_releases_at_the_monorepo_boundary() { + for version in ["0.5.11", "v0.5.11", "guest-os-v0.5.11"] { + let (normalized, url, releases_url) = tagged_release_location(version).unwrap(); + assert_eq!(normalized, "0.5.11"); + assert_eq!( + url, + "https://api.github.com/repos/Dstack-TEE/meta-dstack/releases/tags/v0.5.11" + ); + assert_eq!(releases_url, LEGACY_RELEASES_URL); + } + + for version in ["0.6.0", "0.6.0.a2", "1.0.0"] { + let (normalized, url, releases_url) = tagged_release_location(version).unwrap(); + assert_eq!(normalized, version); + assert_eq!( + url, + format!("https://api.github.com/repos/Dstack-TEE/dstack/releases/tags/guest-os-v{version}") + ); + assert_eq!(releases_url, RELEASES_URL); + } + } + + #[test] + fn rejects_versions_without_a_numeric_core() { + for version in ["0.6", "latest", "0.x.0", "0.6.x"] { + assert!(tagged_release_location(version).is_err(), "{version}"); + } + } + #[test] fn messages_mention_the_pull_command() { assert!(no_image_message("/d").contains("dstackup image pull")); diff --git a/crates/dstackup/src/install.rs b/dstack/crates/dstackup/src/install.rs similarity index 93% rename from crates/dstackup/src/install.rs rename to dstack/crates/dstackup/src/install.rs index afadc9892..70cfd17a5 100644 --- a/crates/dstackup/src/install.rs +++ b/dstack/crates/dstackup/src/install.rs @@ -451,8 +451,24 @@ fn checked_source_checkout(dir: PathBuf) -> Result { if !is_dstack_checkout(&dir) { bail!("{} is not a dstack source checkout", dir.display()); } - dir.canonicalize() - .with_context(|| format!("canonicalizing {}", dir.display())) + let dir = dir + .canonicalize() + .with_context(|| format!("canonicalizing {}", dir.display()))?; + + // Accept either the monorepo root or its dstack/ core directory, but + // normalize new-layout checkouts to the monorepo root so public examples + // and other top-level assets remain reachable. + if is_core_source(&dir) { + if let Some(parent) = dir.parent() { + if parent.join("dstack") == dir + && parent.join("sdk").is_dir() + && parent.join("os").is_dir() + { + return Ok(parent.to_path_buf()); + } + } + } + Ok(dir) } fn sync_source_cache(source: &Path, repo: &str, git_ref: &str) -> Result<()> { @@ -537,7 +553,7 @@ fn git_status_at(dir: &Path, args: [&str; N]) -> Result { .success()) } -fn is_dstack_checkout(dir: &Path) -> bool { +fn is_core_source(dir: &Path) -> bool { dir.join("Cargo.toml").is_file() && dir.join("crates/dstack-cli").is_dir() && dir.join("crates/dstack-auth").is_dir() @@ -545,10 +561,24 @@ fn is_dstack_checkout(dir: &Path) -> bool { && dir.join("supervisor").is_dir() } +fn is_dstack_checkout(dir: &Path) -> bool { + is_core_source(&dir.join("dstack")) || is_core_source(dir) +} + +fn core_source(source: &Path) -> PathBuf { + let nested = source.join("dstack"); + if is_core_source(&nested) { + nested + } else { + source.to_path_buf() + } +} + fn build_managed_binaries(source: &Path, target_dir: &Path) -> Result<()> { + let source = core_source(source); let mut cmd = cargo_build_command(target_dir)?; let target_dir_arg = path_string(target_dir); - cmd.current_dir(source).args([ + cmd.current_dir(&source).args([ "build", "--release", "--target-dir", @@ -710,13 +740,16 @@ fn install_managed_binaries(target_dir: &Path, layout: &InstallLayout) -> Result } fn install_share_assets(source: &Path, layout: &InstallLayout) -> Result<()> { + let core = core_source(source); + let examples = if source.join("examples").is_dir() { + source.join("examples") + } else { + core.join("examples") + }; fs::create_dir_all(&layout.share_dir) .with_context(|| format!("creating {}", layout.share_dir.display()))?; - copy_dir_exact( - &source.join("key-provider-build"), - &layout.key_provider_dir(), - )?; - copy_dir_exact(&source.join("examples"), &layout.share_dir.join("examples"))?; + copy_dir_exact(&core.join("key-provider-build"), &layout.key_provider_dir())?; + copy_dir_exact(&examples, &layout.share_dir.join("examples"))?; println!( " [ok] installed assets into {}", layout.share_dir.display() @@ -1139,6 +1172,14 @@ async fn wait_ready(client_url: &str, timeout: Duration) -> bool { mod tests { use super::*; + fn create_core_tree(root: &Path) { + fs::create_dir_all(root.join("crates/dstack-cli")).unwrap(); + fs::create_dir_all(root.join("crates/dstack-auth")).unwrap(); + fs::create_dir_all(root.join("vmm")).unwrap(); + fs::create_dir_all(root.join("supervisor")).unwrap(); + fs::write(root.join("Cargo.toml"), "[workspace]\n").unwrap(); + } + fn tcp_check(what: &'static str, flag: &'static str, port: u16) -> TcpPortCheck { TcpPortCheck { what, @@ -1160,6 +1201,37 @@ mod tests { assert!(err.contains("--kms-port")); } + #[test] + fn recognizes_and_normalizes_monorepo_source() { + let temp = tempfile::tempdir().unwrap(); + let root = temp.path(); + create_core_tree(&root.join("dstack")); + fs::create_dir(root.join("sdk")).unwrap(); + fs::create_dir(root.join("os")).unwrap(); + fs::create_dir(root.join("examples")).unwrap(); + + assert!(is_dstack_checkout(root)); + assert!(is_dstack_checkout(&root.join("dstack"))); + assert_eq!(core_source(root), root.join("dstack")); + assert_eq!( + checked_source_checkout(root.join("dstack")).unwrap(), + root.canonicalize().unwrap() + ); + } + + #[test] + fn keeps_legacy_core_checkout_compatible() { + let temp = tempfile::tempdir().unwrap(); + create_core_tree(temp.path()); + + assert!(is_dstack_checkout(temp.path())); + assert_eq!(core_source(temp.path()), temp.path()); + assert_eq!( + checked_source_checkout(temp.path().to_path_buf()).unwrap(), + temp.path().canonicalize().unwrap() + ); + } + #[test] fn preflight_rejects_zero_port() { let err = preflight_ports(&[tcp_check("kms", "--kms-port", 0)]) diff --git a/crates/dstackup/src/main.rs b/dstack/crates/dstackup/src/main.rs similarity index 100% rename from crates/dstackup/src/main.rs rename to dstack/crates/dstackup/src/main.rs diff --git a/crates/dstackup/src/state.rs b/dstack/crates/dstackup/src/state.rs similarity index 100% rename from crates/dstackup/src/state.rs rename to dstack/crates/dstackup/src/state.rs diff --git a/crates/dstackup/src/systemd.rs b/dstack/crates/dstackup/src/systemd.rs similarity index 100% rename from crates/dstackup/src/systemd.rs rename to dstack/crates/dstackup/src/systemd.rs diff --git a/ct_monitor/Cargo.toml b/dstack/ct_monitor/Cargo.toml similarity index 100% rename from ct_monitor/Cargo.toml rename to dstack/ct_monitor/Cargo.toml diff --git a/ct_monitor/src/main.rs b/dstack/ct_monitor/src/main.rs similarity index 100% rename from ct_monitor/src/main.rs rename to dstack/ct_monitor/src/main.rs diff --git a/dstack-attest/Cargo.toml b/dstack/dstack-attest/Cargo.toml similarity index 100% rename from dstack-attest/Cargo.toml rename to dstack/dstack-attest/Cargo.toml diff --git a/dstack-attest/src/amd_sev_snp.rs b/dstack/dstack-attest/src/amd_sev_snp.rs similarity index 100% rename from dstack-attest/src/amd_sev_snp.rs rename to dstack/dstack-attest/src/amd_sev_snp.rs diff --git a/dstack-attest/src/attestation.rs b/dstack/dstack-attest/src/attestation.rs similarity index 100% rename from dstack-attest/src/attestation.rs rename to dstack/dstack-attest/src/attestation.rs diff --git a/dstack-attest/src/lib.rs b/dstack/dstack-attest/src/lib.rs similarity index 100% rename from dstack-attest/src/lib.rs rename to dstack/dstack-attest/src/lib.rs diff --git a/dstack-attest/src/sev_snp.rs b/dstack/dstack-attest/src/sev_snp.rs similarity index 100% rename from dstack-attest/src/sev_snp.rs rename to dstack/dstack-attest/src/sev_snp.rs diff --git a/dstack-attest/src/v1.rs b/dstack/dstack-attest/src/v1.rs similarity index 100% rename from dstack-attest/src/v1.rs rename to dstack/dstack-attest/src/v1.rs diff --git a/dstack-attest/tests/nitro_attestation.bin b/dstack/dstack-attest/tests/nitro_attestation.bin similarity index 100% rename from dstack-attest/tests/nitro_attestation.bin rename to dstack/dstack-attest/tests/nitro_attestation.bin diff --git a/dstack-attest/tests/nitro_attestation_dbg.bin b/dstack/dstack-attest/tests/nitro_attestation_dbg.bin similarity index 100% rename from dstack-attest/tests/nitro_attestation_dbg.bin rename to dstack/dstack-attest/tests/nitro_attestation_dbg.bin diff --git a/dstack-attest/tests/nitro_verify.rs b/dstack/dstack-attest/tests/nitro_verify.rs similarity index 100% rename from dstack-attest/tests/nitro_verify.rs rename to dstack/dstack-attest/tests/nitro_verify.rs diff --git a/dstack-attest/tests/sev_snp_ask.pem b/dstack/dstack-attest/tests/sev_snp_ask.pem similarity index 100% rename from dstack-attest/tests/sev_snp_ask.pem rename to dstack/dstack-attest/tests/sev_snp_ask.pem diff --git a/dstack-attest/tests/sev_snp_attestation.bin b/dstack/dstack-attest/tests/sev_snp_attestation.bin similarity index 100% rename from dstack-attest/tests/sev_snp_attestation.bin rename to dstack/dstack-attest/tests/sev_snp_attestation.bin diff --git a/dstack-attest/tests/sev_snp_fixture.README.md b/dstack/dstack-attest/tests/sev_snp_fixture.README.md similarity index 100% rename from dstack-attest/tests/sev_snp_fixture.README.md rename to dstack/dstack-attest/tests/sev_snp_fixture.README.md diff --git a/dstack-attest/tests/sev_snp_vcek.pem b/dstack/dstack-attest/tests/sev_snp_vcek.pem similarity index 100% rename from dstack-attest/tests/sev_snp_vcek.pem rename to dstack/dstack-attest/tests/sev_snp_vcek.pem diff --git a/dstack-attest/tests/sev_snp_verify.rs b/dstack/dstack-attest/tests/sev_snp_verify.rs similarity index 100% rename from dstack-attest/tests/sev_snp_verify.rs rename to dstack/dstack-attest/tests/sev_snp_verify.rs diff --git a/dstack-attest/tests/snapshots/nitro_verify__app_info.snap b/dstack/dstack-attest/tests/snapshots/nitro_verify__app_info.snap similarity index 100% rename from dstack-attest/tests/snapshots/nitro_verify__app_info.snap rename to dstack/dstack-attest/tests/snapshots/nitro_verify__app_info.snap diff --git a/dstack-attest/tests/snapshots/nitro_verify__nitro_report.snap b/dstack/dstack-attest/tests/snapshots/nitro_verify__nitro_report.snap similarity index 100% rename from dstack-attest/tests/snapshots/nitro_verify__nitro_report.snap rename to dstack/dstack-attest/tests/snapshots/nitro_verify__nitro_report.snap diff --git a/dstack-mr/.gitignore b/dstack/dstack-mr/.gitignore similarity index 100% rename from dstack-mr/.gitignore rename to dstack/dstack-mr/.gitignore diff --git a/dstack-mr/Cargo.toml b/dstack/dstack-mr/Cargo.toml similarity index 100% rename from dstack-mr/Cargo.toml rename to dstack/dstack-mr/Cargo.toml diff --git a/dstack-mr/cli/Cargo.toml b/dstack/dstack-mr/cli/Cargo.toml similarity index 100% rename from dstack-mr/cli/Cargo.toml rename to dstack/dstack-mr/cli/Cargo.toml diff --git a/dstack-mr/cli/src/main.rs b/dstack/dstack-mr/cli/src/main.rs similarity index 100% rename from dstack-mr/cli/src/main.rs rename to dstack/dstack-mr/cli/src/main.rs diff --git a/dstack-mr/src/acpi.rs b/dstack/dstack-mr/src/acpi.rs similarity index 100% rename from dstack-mr/src/acpi.rs rename to dstack/dstack-mr/src/acpi.rs diff --git a/dstack-mr/src/kernel.rs b/dstack/dstack-mr/src/kernel.rs similarity index 100% rename from dstack-mr/src/kernel.rs rename to dstack/dstack-mr/src/kernel.rs diff --git a/dstack-mr/src/lib.rs b/dstack/dstack-mr/src/lib.rs similarity index 100% rename from dstack-mr/src/lib.rs rename to dstack/dstack-mr/src/lib.rs diff --git a/dstack-mr/src/machine.rs b/dstack/dstack-mr/src/machine.rs similarity index 100% rename from dstack-mr/src/machine.rs rename to dstack/dstack-mr/src/machine.rs diff --git a/dstack-mr/src/main.rs b/dstack/dstack-mr/src/main.rs similarity index 100% rename from dstack-mr/src/main.rs rename to dstack/dstack-mr/src/main.rs diff --git a/dstack-mr/src/measurement.rs b/dstack/dstack-mr/src/measurement.rs similarity index 100% rename from dstack-mr/src/measurement.rs rename to dstack/dstack-mr/src/measurement.rs diff --git a/dstack-mr/src/num.rs b/dstack/dstack-mr/src/num.rs similarity index 100% rename from dstack-mr/src/num.rs rename to dstack/dstack-mr/src/num.rs diff --git a/dstack-mr/src/sev.rs b/dstack/dstack-mr/src/sev.rs similarity index 100% rename from dstack-mr/src/sev.rs rename to dstack/dstack-mr/src/sev.rs diff --git a/dstack-mr/src/tdvf.rs b/dstack/dstack-mr/src/tdvf.rs similarity index 100% rename from dstack-mr/src/tdvf.rs rename to dstack/dstack-mr/src/tdvf.rs diff --git a/dstack-mr/src/tdx.rs b/dstack/dstack-mr/src/tdx.rs similarity index 100% rename from dstack-mr/src/tdx.rs rename to dstack/dstack-mr/src/tdx.rs diff --git a/dstack-mr/src/util.rs b/dstack/dstack-mr/src/util.rs similarity index 100% rename from dstack-mr/src/util.rs rename to dstack/dstack-mr/src/util.rs diff --git a/dstack-mr/tests/tdvf_parse.rs b/dstack/dstack-mr/tests/tdvf_parse.rs similarity index 100% rename from dstack-mr/tests/tdvf_parse.rs rename to dstack/dstack-mr/tests/tdvf_parse.rs diff --git a/dstack-types/Cargo.toml b/dstack/dstack-types/Cargo.toml similarity index 100% rename from dstack-types/Cargo.toml rename to dstack/dstack-types/Cargo.toml diff --git a/dstack-types/src/lib.rs b/dstack/dstack-types/src/lib.rs similarity index 100% rename from dstack-types/src/lib.rs rename to dstack/dstack-types/src/lib.rs diff --git a/dstack-types/src/mr_config.rs b/dstack/dstack-types/src/mr_config.rs similarity index 100% rename from dstack-types/src/mr_config.rs rename to dstack/dstack-types/src/mr_config.rs diff --git a/dstack-types/src/shared_filenames.rs b/dstack/dstack-types/src/shared_filenames.rs similarity index 100% rename from dstack-types/src/shared_filenames.rs rename to dstack/dstack-types/src/shared_filenames.rs diff --git a/dstack-types/src/version.rs b/dstack/dstack-types/src/version.rs similarity index 100% rename from dstack-types/src/version.rs rename to dstack/dstack-types/src/version.rs diff --git a/dstack-util/Cargo.toml b/dstack/dstack-util/Cargo.toml similarity index 100% rename from dstack-util/Cargo.toml rename to dstack/dstack-util/Cargo.toml diff --git a/dstack-util/src/crypto.rs b/dstack/dstack-util/src/crypto.rs similarity index 100% rename from dstack-util/src/crypto.rs rename to dstack/dstack-util/src/crypto.rs diff --git a/dstack-util/src/docker_compose.rs b/dstack/dstack-util/src/docker_compose.rs similarity index 100% rename from dstack-util/src/docker_compose.rs rename to dstack/dstack-util/src/docker_compose.rs diff --git a/dstack-util/src/host_api.rs b/dstack/dstack-util/src/host_api.rs similarity index 100% rename from dstack-util/src/host_api.rs rename to dstack/dstack-util/src/host_api.rs diff --git a/dstack-util/src/main.rs b/dstack/dstack-util/src/main.rs similarity index 100% rename from dstack-util/src/main.rs rename to dstack/dstack-util/src/main.rs diff --git a/dstack-util/src/parse_env_file.rs b/dstack/dstack-util/src/parse_env_file.rs similarity index 100% rename from dstack-util/src/parse_env_file.rs rename to dstack/dstack-util/src/parse_env_file.rs diff --git a/dstack-util/src/system_setup.rs b/dstack/dstack-util/src/system_setup.rs similarity index 100% rename from dstack-util/src/system_setup.rs rename to dstack/dstack-util/src/system_setup.rs diff --git a/dstack-util/src/system_setup/config_id_verifier.rs b/dstack/dstack-util/src/system_setup/config_id_verifier.rs similarity index 100% rename from dstack-util/src/system_setup/config_id_verifier.rs rename to dstack/dstack-util/src/system_setup/config_id_verifier.rs diff --git a/dstack-util/src/utils.rs b/dstack/dstack-util/src/utils.rs similarity index 100% rename from dstack-util/src/utils.rs rename to dstack/dstack-util/src/utils.rs diff --git a/dstack-util/tests/fixtures/key-provider-docker-compose.yaml b/dstack/dstack-util/tests/fixtures/key-provider-docker-compose.yaml similarity index 100% rename from dstack-util/tests/fixtures/key-provider-docker-compose.yaml rename to dstack/dstack-util/tests/fixtures/key-provider-docker-compose.yaml diff --git a/dstack-util/tests/fixtures/luks_header_cipher_null b/dstack/dstack-util/tests/fixtures/luks_header_cipher_null similarity index 100% rename from dstack-util/tests/fixtures/luks_header_cipher_null rename to dstack/dstack-util/tests/fixtures/luks_header_cipher_null diff --git a/dstack-util/tests/fixtures/luks_header_cipher_null.license b/dstack/dstack-util/tests/fixtures/luks_header_cipher_null.license similarity index 66% rename from dstack-util/tests/fixtures/luks_header_cipher_null.license rename to dstack/dstack-util/tests/fixtures/luks_header_cipher_null.license index 8f861f09b..84ac4efa8 100644 --- a/dstack-util/tests/fixtures/luks_header_cipher_null.license +++ b/dstack/dstack-util/tests/fixtures/luks_header_cipher_null.license @@ -1,3 +1,3 @@ SPDX-FileCopyrightText: © 2025 Phala Network -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 diff --git a/dstack-util/tests/fixtures/luks_header_good b/dstack/dstack-util/tests/fixtures/luks_header_good similarity index 100% rename from dstack-util/tests/fixtures/luks_header_good rename to dstack/dstack-util/tests/fixtures/luks_header_good diff --git a/dstack-util/tests/fixtures/luks_header_good.license b/dstack/dstack-util/tests/fixtures/luks_header_good.license similarity index 66% rename from dstack-util/tests/fixtures/luks_header_good.license rename to dstack/dstack-util/tests/fixtures/luks_header_good.license index 8f861f09b..84ac4efa8 100644 --- a/dstack-util/tests/fixtures/luks_header_good.license +++ b/dstack/dstack-util/tests/fixtures/luks_header_good.license @@ -1,3 +1,3 @@ SPDX-FileCopyrightText: © 2025 Phala Network -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +SPDX-License-Identifier: Apache-2.0 diff --git a/dstack-util/tests/test_remove_orphans.sh b/dstack/dstack-util/tests/test_remove_orphans.sh similarity index 100% rename from dstack-util/tests/test_remove_orphans.sh rename to dstack/dstack-util/tests/test_remove_orphans.sh diff --git a/gateway/Cargo.toml b/dstack/gateway/Cargo.toml similarity index 100% rename from gateway/Cargo.toml rename to dstack/gateway/Cargo.toml diff --git a/gateway/assets/cert.key b/dstack/gateway/assets/cert.key similarity index 100% rename from gateway/assets/cert.key rename to dstack/gateway/assets/cert.key diff --git a/gateway/assets/cert.pem b/dstack/gateway/assets/cert.pem similarity index 100% rename from gateway/assets/cert.pem rename to dstack/gateway/assets/cert.pem diff --git a/gateway/docs/cluster-deployment.md b/dstack/gateway/docs/cluster-deployment.md similarity index 100% rename from gateway/docs/cluster-deployment.md rename to dstack/gateway/docs/cluster-deployment.md diff --git a/gateway/dstack-app/.gitignore b/dstack/gateway/dstack-app/.gitignore similarity index 100% rename from gateway/dstack-app/.gitignore rename to dstack/gateway/dstack-app/.gitignore diff --git a/gateway/dstack-app/bootstrap-cluster.sh b/dstack/gateway/dstack-app/bootstrap-cluster.sh similarity index 100% rename from gateway/dstack-app/bootstrap-cluster.sh rename to dstack/gateway/dstack-app/bootstrap-cluster.sh diff --git a/gateway/dstack-app/builder/Dockerfile b/dstack/gateway/dstack-app/builder/Dockerfile similarity index 83% rename from gateway/dstack-app/builder/Dockerfile rename to dstack/gateway/dstack-app/builder/Dockerfile index 638017be5..e91b00486 100644 --- a/gateway/dstack-app/builder/Dockerfile +++ b/dstack/gateway/dstack-app/builder/Dockerfile @@ -19,11 +19,11 @@ RUN apt-get update && \ libprotobuf-dev \ clang \ libclang-dev -RUN git clone ${DSTACK_SRC_URL} dstack && \ - cd dstack && \ +RUN git clone ${DSTACK_SRC_URL} repo && \ + cd repo && \ git checkout ${DSTACK_REV} RUN rustup target add x86_64-unknown-linux-musl -RUN cd dstack && cargo build --release -p dstack-gateway --target x86_64-unknown-linux-musl +RUN cd repo/dstack && cargo build --release -p dstack-gateway --target x86_64-unknown-linux-musl RUN echo "${DSTACK_REV}" > /build/.GIT_REV FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe @@ -39,7 +39,7 @@ RUN ./pin-packages.sh ./pinned-packages.txt && \ jq \ ca-certificates \ && rm -rf /var/lib/apt/lists/* /var/log/* /var/cache/ldconfig/aux-cache -COPY --from=gateway-builder /build/dstack/target/x86_64-unknown-linux-musl/release/dstack-gateway /usr/local/bin/dstack-gateway +COPY --from=gateway-builder /build/repo/dstack/target/x86_64-unknown-linux-musl/release/dstack-gateway /usr/local/bin/dstack-gateway COPY --from=gateway-builder /build/.GIT_REV /etc/ WORKDIR /app COPY entrypoint.sh /app/entrypoint.sh diff --git a/gateway/dstack-app/builder/README.md b/dstack/gateway/dstack-app/builder/README.md similarity index 100% rename from gateway/dstack-app/builder/README.md rename to dstack/gateway/dstack-app/builder/README.md diff --git a/gateway/dstack-app/builder/build-image.sh b/dstack/gateway/dstack-app/builder/build-image.sh similarity index 94% rename from gateway/dstack-app/builder/build-image.sh rename to dstack/gateway/dstack-app/builder/build-image.sh index 685e2019f..3d86546f1 100755 --- a/gateway/dstack-app/builder/build-image.sh +++ b/dstack/gateway/dstack-app/builder/build-image.sh @@ -12,7 +12,7 @@ CONTEXT_DIR="$SCRIPT_DIR" SHARED_DIR="$SCRIPT_DIR/shared" DOCKERFILE="$SCRIPT_DIR/Dockerfile" -source "$REPO_ROOT/build/shared/build-lib.sh" +source "$REPO_ROOT/dstack/build/shared/build-lib.sh" NAME=${1:-} if [ -z "$NAME" ]; then diff --git a/gateway/dstack-app/builder/entrypoint.sh b/dstack/gateway/dstack-app/builder/entrypoint.sh similarity index 100% rename from gateway/dstack-app/builder/entrypoint.sh rename to dstack/gateway/dstack-app/builder/entrypoint.sh diff --git a/gateway/dstack-app/builder/shared/builder-pinned-packages.txt b/dstack/gateway/dstack-app/builder/shared/builder-pinned-packages.txt similarity index 100% rename from gateway/dstack-app/builder/shared/builder-pinned-packages.txt rename to dstack/gateway/dstack-app/builder/shared/builder-pinned-packages.txt diff --git a/gateway/dstack-app/builder/shared/pinned-packages.txt b/dstack/gateway/dstack-app/builder/shared/pinned-packages.txt similarity index 100% rename from gateway/dstack-app/builder/shared/pinned-packages.txt rename to dstack/gateway/dstack-app/builder/shared/pinned-packages.txt diff --git a/gateway/dstack-app/deploy-to-vmm.sh b/dstack/gateway/dstack-app/deploy-to-vmm.sh similarity index 100% rename from gateway/dstack-app/deploy-to-vmm.sh rename to dstack/gateway/dstack-app/deploy-to-vmm.sh diff --git a/gateway/dstack-app/docker-compose.yaml b/dstack/gateway/dstack-app/docker-compose.yaml similarity index 100% rename from gateway/dstack-app/docker-compose.yaml rename to dstack/gateway/dstack-app/docker-compose.yaml diff --git a/gateway/gateway.toml b/dstack/gateway/gateway.toml similarity index 100% rename from gateway/gateway.toml rename to dstack/gateway/gateway.toml diff --git a/gateway/rpc/Cargo.toml b/dstack/gateway/rpc/Cargo.toml similarity index 100% rename from gateway/rpc/Cargo.toml rename to dstack/gateway/rpc/Cargo.toml diff --git a/gateway/rpc/build.rs b/dstack/gateway/rpc/build.rs similarity index 100% rename from gateway/rpc/build.rs rename to dstack/gateway/rpc/build.rs diff --git a/gateway/rpc/proto/gateway_rpc.proto b/dstack/gateway/rpc/proto/gateway_rpc.proto similarity index 100% rename from gateway/rpc/proto/gateway_rpc.proto rename to dstack/gateway/rpc/proto/gateway_rpc.proto diff --git a/gateway/rpc/src/generated.rs b/dstack/gateway/rpc/src/generated.rs similarity index 100% rename from gateway/rpc/src/generated.rs rename to dstack/gateway/rpc/src/generated.rs diff --git a/gateway/rpc/src/lib.rs b/dstack/gateway/rpc/src/lib.rs similarity index 100% rename from gateway/rpc/src/lib.rs rename to dstack/gateway/rpc/src/lib.rs diff --git a/gateway/src/admin_auth.rs b/dstack/gateway/src/admin_auth.rs similarity index 100% rename from gateway/src/admin_auth.rs rename to dstack/gateway/src/admin_auth.rs diff --git a/gateway/src/admin_service.rs b/dstack/gateway/src/admin_service.rs similarity index 100% rename from gateway/src/admin_service.rs rename to dstack/gateway/src/admin_service.rs diff --git a/gateway/src/cert_store.rs b/dstack/gateway/src/cert_store.rs similarity index 100% rename from gateway/src/cert_store.rs rename to dstack/gateway/src/cert_store.rs diff --git a/gateway/src/config.rs b/dstack/gateway/src/config.rs similarity index 100% rename from gateway/src/config.rs rename to dstack/gateway/src/config.rs diff --git a/gateway/src/debug_service.rs b/dstack/gateway/src/debug_service.rs similarity index 100% rename from gateway/src/debug_service.rs rename to dstack/gateway/src/debug_service.rs diff --git a/gateway/src/distributed_certbot.rs b/dstack/gateway/src/distributed_certbot.rs similarity index 100% rename from gateway/src/distributed_certbot.rs rename to dstack/gateway/src/distributed_certbot.rs diff --git a/gateway/src/gen_debug_key.rs b/dstack/gateway/src/gen_debug_key.rs similarity index 100% rename from gateway/src/gen_debug_key.rs rename to dstack/gateway/src/gen_debug_key.rs diff --git a/gateway/src/kv/https_client.rs b/dstack/gateway/src/kv/https_client.rs similarity index 100% rename from gateway/src/kv/https_client.rs rename to dstack/gateway/src/kv/https_client.rs diff --git a/gateway/src/kv/mod.rs b/dstack/gateway/src/kv/mod.rs similarity index 100% rename from gateway/src/kv/mod.rs rename to dstack/gateway/src/kv/mod.rs diff --git a/gateway/src/kv/sync_service.rs b/dstack/gateway/src/kv/sync_service.rs similarity index 100% rename from gateway/src/kv/sync_service.rs rename to dstack/gateway/src/kv/sync_service.rs diff --git a/gateway/src/main.rs b/dstack/gateway/src/main.rs similarity index 100% rename from gateway/src/main.rs rename to dstack/gateway/src/main.rs diff --git a/gateway/src/main_service.rs b/dstack/gateway/src/main_service.rs similarity index 100% rename from gateway/src/main_service.rs rename to dstack/gateway/src/main_service.rs diff --git a/gateway/src/main_service/auth_client.rs b/dstack/gateway/src/main_service/auth_client.rs similarity index 100% rename from gateway/src/main_service/auth_client.rs rename to dstack/gateway/src/main_service/auth_client.rs diff --git a/gateway/src/main_service/handshakes.rs b/dstack/gateway/src/main_service/handshakes.rs similarity index 100% rename from gateway/src/main_service/handshakes.rs rename to dstack/gateway/src/main_service/handshakes.rs diff --git a/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-2.snap b/dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-2.snap similarity index 100% rename from gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-2.snap rename to dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-2.snap diff --git a/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-3.snap b/dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-3.snap similarity index 100% rename from gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-3.snap rename to dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config-3.snap diff --git a/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config.snap b/dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config.snap similarity index 100% rename from gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config.snap rename to dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__config.snap diff --git a/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__empty_config.snap b/dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__empty_config.snap similarity index 100% rename from gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__empty_config.snap rename to dstack/gateway/src/main_service/snapshots/dstack_gateway__main_service__tests__empty_config.snap diff --git a/gateway/src/main_service/tests.rs b/dstack/gateway/src/main_service/tests.rs similarity index 100% rename from gateway/src/main_service/tests.rs rename to dstack/gateway/src/main_service/tests.rs diff --git a/gateway/src/models.rs b/dstack/gateway/src/models.rs similarity index 100% rename from gateway/src/models.rs rename to dstack/gateway/src/models.rs diff --git a/gateway/src/pp.rs b/dstack/gateway/src/pp.rs similarity index 100% rename from gateway/src/pp.rs rename to dstack/gateway/src/pp.rs diff --git a/gateway/src/proxy.rs b/dstack/gateway/src/proxy.rs similarity index 100% rename from gateway/src/proxy.rs rename to dstack/gateway/src/proxy.rs diff --git a/gateway/src/proxy/io_bridge.rs b/dstack/gateway/src/proxy/io_bridge.rs similarity index 100% rename from gateway/src/proxy/io_bridge.rs rename to dstack/gateway/src/proxy/io_bridge.rs diff --git a/gateway/src/proxy/port_policy.rs b/dstack/gateway/src/proxy/port_policy.rs similarity index 100% rename from gateway/src/proxy/port_policy.rs rename to dstack/gateway/src/proxy/port_policy.rs diff --git a/gateway/src/proxy/sni.rs b/dstack/gateway/src/proxy/sni.rs similarity index 100% rename from gateway/src/proxy/sni.rs rename to dstack/gateway/src/proxy/sni.rs diff --git a/gateway/src/proxy/tls_passthough.rs b/dstack/gateway/src/proxy/tls_passthough.rs similarity index 100% rename from gateway/src/proxy/tls_passthough.rs rename to dstack/gateway/src/proxy/tls_passthough.rs diff --git a/gateway/src/proxy/tls_terminate.rs b/dstack/gateway/src/proxy/tls_terminate.rs similarity index 100% rename from gateway/src/proxy/tls_terminate.rs rename to dstack/gateway/src/proxy/tls_terminate.rs diff --git a/gateway/src/web_routes.rs b/dstack/gateway/src/web_routes.rs similarity index 100% rename from gateway/src/web_routes.rs rename to dstack/gateway/src/web_routes.rs diff --git a/gateway/src/web_routes/route_index.rs b/dstack/gateway/src/web_routes/route_index.rs similarity index 100% rename from gateway/src/web_routes/route_index.rs rename to dstack/gateway/src/web_routes/route_index.rs diff --git a/gateway/src/web_routes/wavekv_sync.rs b/dstack/gateway/src/web_routes/wavekv_sync.rs similarity index 100% rename from gateway/src/web_routes/wavekv_sync.rs rename to dstack/gateway/src/web_routes/wavekv_sync.rs diff --git a/gateway/templates/dashboard.html b/dstack/gateway/templates/dashboard.html similarity index 99% rename from gateway/templates/dashboard.html rename to dstack/gateway/templates/dashboard.html index 72c600bff..55a296ff8 100644 --- a/gateway/templates/dashboard.html +++ b/dstack/gateway/templates/dashboard.html @@ -1545,4 +1545,4 @@

Add ZT-Domain

- \ No newline at end of file + diff --git a/gateway/templates/rproxy.yaml b/dstack/gateway/templates/rproxy.yaml similarity index 97% rename from gateway/templates/rproxy.yaml rename to dstack/gateway/templates/rproxy.yaml index 69d03792b..e5513b0ae 100644 --- a/gateway/templates/rproxy.yaml +++ b/dstack/gateway/templates/rproxy.yaml @@ -22,4 +22,4 @@ servers: target: {{ peer.ip }}:{{ p.target_port }} {% endfor %} {%- endif %} - {%- endfor %} \ No newline at end of file + {%- endfor %} diff --git a/gateway/templates/wg.conf b/dstack/gateway/templates/wg.conf similarity index 93% rename from gateway/templates/wg.conf rename to dstack/gateway/templates/wg.conf index 3a5e4acaa..eb1d39e6a 100644 --- a/gateway/templates/wg.conf +++ b/dstack/gateway/templates/wg.conf @@ -7,4 +7,4 @@ ListenPort = {{ listen_port }} PublicKey = {{ peer.public_key }} AllowedIPs = {{ peer.ip }}/32 PersistentKeepalive = 25 -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/gateway/test-run/.env.example b/dstack/gateway/test-run/.env.example similarity index 100% rename from gateway/test-run/.env.example rename to dstack/gateway/test-run/.env.example diff --git a/gateway/test-run/.gitignore b/dstack/gateway/test-run/.gitignore similarity index 100% rename from gateway/test-run/.gitignore rename to dstack/gateway/test-run/.gitignore diff --git a/gateway/test-run/TESTING.md b/dstack/gateway/test-run/TESTING.md similarity index 100% rename from gateway/test-run/TESTING.md rename to dstack/gateway/test-run/TESTING.md diff --git a/gateway/test-run/cluster.sh b/dstack/gateway/test-run/cluster.sh similarity index 100% rename from gateway/test-run/cluster.sh rename to dstack/gateway/test-run/cluster.sh diff --git a/gateway/test-run/e2e/configs/gateway-1.toml b/dstack/gateway/test-run/e2e/configs/gateway-1.toml similarity index 100% rename from gateway/test-run/e2e/configs/gateway-1.toml rename to dstack/gateway/test-run/e2e/configs/gateway-1.toml diff --git a/gateway/test-run/e2e/configs/gateway-2.toml b/dstack/gateway/test-run/e2e/configs/gateway-2.toml similarity index 100% rename from gateway/test-run/e2e/configs/gateway-2.toml rename to dstack/gateway/test-run/e2e/configs/gateway-2.toml diff --git a/gateway/test-run/e2e/configs/gateway-3.toml b/dstack/gateway/test-run/e2e/configs/gateway-3.toml similarity index 100% rename from gateway/test-run/e2e/configs/gateway-3.toml rename to dstack/gateway/test-run/e2e/configs/gateway-3.toml diff --git a/gateway/test-run/e2e/docker-compose.yml b/dstack/gateway/test-run/e2e/docker-compose.yml similarity index 100% rename from gateway/test-run/e2e/docker-compose.yml rename to dstack/gateway/test-run/e2e/docker-compose.yml diff --git a/gateway/test-run/e2e/pebble-config.json b/dstack/gateway/test-run/e2e/pebble-config.json similarity index 100% rename from gateway/test-run/e2e/pebble-config.json rename to dstack/gateway/test-run/e2e/pebble-config.json diff --git a/gateway/test-run/e2e/run-e2e.sh b/dstack/gateway/test-run/e2e/run-e2e.sh similarity index 100% rename from gateway/test-run/e2e/run-e2e.sh rename to dstack/gateway/test-run/e2e/run-e2e.sh diff --git a/gateway/test-run/e2e/test.sh b/dstack/gateway/test-run/e2e/test.sh similarity index 100% rename from gateway/test-run/e2e/test.sh rename to dstack/gateway/test-run/e2e/test.sh diff --git a/gateway/test-run/test_certbot.sh b/dstack/gateway/test-run/test_certbot.sh similarity index 100% rename from gateway/test-run/test_certbot.sh rename to dstack/gateway/test-run/test_certbot.sh diff --git a/gateway/test-run/test_suite.sh b/dstack/gateway/test-run/test_suite.sh similarity index 100% rename from gateway/test-run/test_suite.sh rename to dstack/gateway/test-run/test_suite.sh diff --git a/guest-agent-simulator/.gitignore b/dstack/guest-agent-simulator/.gitignore similarity index 100% rename from guest-agent-simulator/.gitignore rename to dstack/guest-agent-simulator/.gitignore diff --git a/guest-agent-simulator/Cargo.toml b/dstack/guest-agent-simulator/Cargo.toml similarity index 100% rename from guest-agent-simulator/Cargo.toml rename to dstack/guest-agent-simulator/Cargo.toml diff --git a/guest-agent-simulator/dstack-simulator.service b/dstack/guest-agent-simulator/dstack-simulator.service similarity index 100% rename from guest-agent-simulator/dstack-simulator.service rename to dstack/guest-agent-simulator/dstack-simulator.service diff --git a/guest-agent-simulator/dstack.toml b/dstack/guest-agent-simulator/dstack.toml similarity index 100% rename from guest-agent-simulator/dstack.toml rename to dstack/guest-agent-simulator/dstack.toml diff --git a/guest-agent-simulator/install-systemd.sh b/dstack/guest-agent-simulator/install-systemd.sh similarity index 100% rename from guest-agent-simulator/install-systemd.sh rename to dstack/guest-agent-simulator/install-systemd.sh diff --git a/guest-agent-simulator/package-release.sh b/dstack/guest-agent-simulator/package-release.sh similarity index 76% rename from guest-agent-simulator/package-release.sh rename to dstack/guest-agent-simulator/package-release.sh index 5223eda66..d1cd3892f 100755 --- a/guest-agent-simulator/package-release.sh +++ b/dstack/guest-agent-simulator/package-release.sh @@ -28,9 +28,10 @@ TARGET="$2" shift 2 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CORE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$CORE_DIR/.." && pwd)" OUT_DIR="$SCRIPT_DIR/dist" -BINARY_PATH="$ROOT_DIR/target/$TARGET/release/dstack-simulator" +BINARY_PATH="$CORE_DIR/target/$TARGET/release/dstack-simulator" while [[ $# -gt 0 ]]; do case "$1" in @@ -68,11 +69,11 @@ rm -rf "$STAGE_DIR" "$TARBALL_PATH" "$CHECKSUM_PATH" mkdir -p "$STAGE_DIR" install -m 755 "$BINARY_PATH" "$STAGE_DIR/dstack-simulator" -install -m 644 "$ROOT_DIR/sdk/simulator/dstack.toml" "$STAGE_DIR/dstack.toml" -install -m 644 "$ROOT_DIR/sdk/simulator/app-compose.json" "$STAGE_DIR/app-compose.json" -install -m 644 "$ROOT_DIR/sdk/simulator/appkeys.json" "$STAGE_DIR/appkeys.json" -install -m 644 "$ROOT_DIR/sdk/simulator/sys-config.json" "$STAGE_DIR/sys-config.json" -install -m 644 "$ROOT_DIR/sdk/simulator/attestation.bin" "$STAGE_DIR/attestation.bin" +install -m 644 "$REPO_ROOT/sdk/simulator/dstack.toml" "$STAGE_DIR/dstack.toml" +install -m 644 "$REPO_ROOT/sdk/simulator/app-compose.json" "$STAGE_DIR/app-compose.json" +install -m 644 "$REPO_ROOT/sdk/simulator/appkeys.json" "$STAGE_DIR/appkeys.json" +install -m 644 "$REPO_ROOT/sdk/simulator/sys-config.json" "$STAGE_DIR/sys-config.json" +install -m 644 "$REPO_ROOT/sdk/simulator/attestation.bin" "$STAGE_DIR/attestation.bin" install -m 644 "$SCRIPT_DIR/dstack-simulator.service" "$STAGE_DIR/dstack-simulator.service" install -m 755 "$SCRIPT_DIR/install-systemd.sh" "$STAGE_DIR/install-systemd.sh" diff --git a/guest-agent-simulator/src/main.rs b/dstack/guest-agent-simulator/src/main.rs similarity index 100% rename from guest-agent-simulator/src/main.rs rename to dstack/guest-agent-simulator/src/main.rs diff --git a/guest-agent-simulator/src/simulator.rs b/dstack/guest-agent-simulator/src/simulator.rs similarity index 100% rename from guest-agent-simulator/src/simulator.rs rename to dstack/guest-agent-simulator/src/simulator.rs diff --git a/guest-agent/Cargo.toml b/dstack/guest-agent/Cargo.toml similarity index 100% rename from guest-agent/Cargo.toml rename to dstack/guest-agent/Cargo.toml diff --git a/guest-agent/dstack.toml b/dstack/guest-agent/dstack.toml similarity index 100% rename from guest-agent/dstack.toml rename to dstack/guest-agent/dstack.toml diff --git a/guest-agent/fixtures/attestation.bin b/dstack/guest-agent/fixtures/attestation.bin similarity index 100% rename from guest-agent/fixtures/attestation.bin rename to dstack/guest-agent/fixtures/attestation.bin diff --git a/guest-agent/rpc/Cargo.toml b/dstack/guest-agent/rpc/Cargo.toml similarity index 100% rename from guest-agent/rpc/Cargo.toml rename to dstack/guest-agent/rpc/Cargo.toml diff --git a/guest-agent/rpc/build.rs b/dstack/guest-agent/rpc/build.rs similarity index 100% rename from guest-agent/rpc/build.rs rename to dstack/guest-agent/rpc/build.rs diff --git a/guest-agent/rpc/proto/agent_rpc.proto b/dstack/guest-agent/rpc/proto/agent_rpc.proto similarity index 100% rename from guest-agent/rpc/proto/agent_rpc.proto rename to dstack/guest-agent/rpc/proto/agent_rpc.proto diff --git a/guest-agent/rpc/src/generated.rs b/dstack/guest-agent/rpc/src/generated.rs similarity index 100% rename from guest-agent/rpc/src/generated.rs rename to dstack/guest-agent/rpc/src/generated.rs diff --git a/guest-agent/rpc/src/lib.rs b/dstack/guest-agent/rpc/src/lib.rs similarity index 100% rename from guest-agent/rpc/src/lib.rs rename to dstack/guest-agent/rpc/src/lib.rs diff --git a/guest-agent/src/backend.rs b/dstack/guest-agent/src/backend.rs similarity index 100% rename from guest-agent/src/backend.rs rename to dstack/guest-agent/src/backend.rs diff --git a/guest-agent/src/config.rs b/dstack/guest-agent/src/config.rs similarity index 100% rename from guest-agent/src/config.rs rename to dstack/guest-agent/src/config.rs diff --git a/guest-agent/src/guest_api_service.rs b/dstack/guest-agent/src/guest_api_service.rs similarity index 100% rename from guest-agent/src/guest_api_service.rs rename to dstack/guest-agent/src/guest_api_service.rs diff --git a/guest-agent/src/http_routes.rs b/dstack/guest-agent/src/http_routes.rs similarity index 100% rename from guest-agent/src/http_routes.rs rename to dstack/guest-agent/src/http_routes.rs diff --git a/guest-agent/src/lib.rs b/dstack/guest-agent/src/lib.rs similarity index 100% rename from guest-agent/src/lib.rs rename to dstack/guest-agent/src/lib.rs diff --git a/guest-agent/src/main.rs b/dstack/guest-agent/src/main.rs similarity index 100% rename from guest-agent/src/main.rs rename to dstack/guest-agent/src/main.rs diff --git a/guest-agent/src/models.rs b/dstack/guest-agent/src/models.rs similarity index 100% rename from guest-agent/src/models.rs rename to dstack/guest-agent/src/models.rs diff --git a/guest-agent/src/rpc_service.rs b/dstack/guest-agent/src/rpc_service.rs similarity index 100% rename from guest-agent/src/rpc_service.rs rename to dstack/guest-agent/src/rpc_service.rs diff --git a/guest-agent/src/server.rs b/dstack/guest-agent/src/server.rs similarity index 100% rename from guest-agent/src/server.rs rename to dstack/guest-agent/src/server.rs diff --git a/guest-agent/src/socket_activation.rs b/dstack/guest-agent/src/socket_activation.rs similarity index 100% rename from guest-agent/src/socket_activation.rs rename to dstack/guest-agent/src/socket_activation.rs diff --git a/guest-agent/templates/dashboard.html b/dstack/guest-agent/templates/dashboard.html similarity index 99% rename from guest-agent/templates/dashboard.html rename to dstack/guest-agent/templates/dashboard.html index 212df06ac..4cfd5494d 100644 --- a/guest-agent/templates/dashboard.html +++ b/dstack/guest-agent/templates/dashboard.html @@ -251,4 +251,4 @@

TCB Info

- \ No newline at end of file + diff --git a/guest-agent/templates/metrics.tpl b/dstack/guest-agent/templates/metrics.tpl similarity index 100% rename from guest-agent/templates/metrics.tpl rename to dstack/guest-agent/templates/metrics.tpl diff --git a/guest-api/Cargo.toml b/dstack/guest-api/Cargo.toml similarity index 100% rename from guest-api/Cargo.toml rename to dstack/guest-api/Cargo.toml diff --git a/guest-api/build.rs b/dstack/guest-api/build.rs similarity index 100% rename from guest-api/build.rs rename to dstack/guest-api/build.rs diff --git a/guest-api/proto/guest_api.proto b/dstack/guest-api/proto/guest_api.proto similarity index 100% rename from guest-api/proto/guest_api.proto rename to dstack/guest-api/proto/guest_api.proto diff --git a/guest-api/src/client.rs b/dstack/guest-api/src/client.rs similarity index 100% rename from guest-api/src/client.rs rename to dstack/guest-api/src/client.rs diff --git a/guest-api/src/generated/mod.rs b/dstack/guest-api/src/generated/mod.rs similarity index 100% rename from guest-api/src/generated/mod.rs rename to dstack/guest-api/src/generated/mod.rs diff --git a/guest-api/src/lib.rs b/dstack/guest-api/src/lib.rs similarity index 100% rename from guest-api/src/lib.rs rename to dstack/guest-api/src/lib.rs diff --git a/host-api/Cargo.toml b/dstack/host-api/Cargo.toml similarity index 100% rename from host-api/Cargo.toml rename to dstack/host-api/Cargo.toml diff --git a/host-api/build.rs b/dstack/host-api/build.rs similarity index 100% rename from host-api/build.rs rename to dstack/host-api/build.rs diff --git a/host-api/proto/host_api.proto b/dstack/host-api/proto/host_api.proto similarity index 100% rename from host-api/proto/host_api.proto rename to dstack/host-api/proto/host_api.proto diff --git a/host-api/src/client.rs b/dstack/host-api/src/client.rs similarity index 100% rename from host-api/src/client.rs rename to dstack/host-api/src/client.rs diff --git a/host-api/src/generated/mod.rs b/dstack/host-api/src/generated/mod.rs similarity index 100% rename from host-api/src/generated/mod.rs rename to dstack/host-api/src/generated/mod.rs diff --git a/host-api/src/lib.rs b/dstack/host-api/src/lib.rs similarity index 100% rename from host-api/src/lib.rs rename to dstack/host-api/src/lib.rs diff --git a/http-client/Cargo.toml b/dstack/http-client/Cargo.toml similarity index 100% rename from http-client/Cargo.toml rename to dstack/http-client/Cargo.toml diff --git a/http-client/src/hyper_vsock.rs b/dstack/http-client/src/hyper_vsock.rs similarity index 100% rename from http-client/src/hyper_vsock.rs rename to dstack/http-client/src/hyper_vsock.rs diff --git a/http-client/src/lib.rs b/dstack/http-client/src/lib.rs similarity index 100% rename from http-client/src/lib.rs rename to dstack/http-client/src/lib.rs diff --git a/http-client/src/prpc.rs b/dstack/http-client/src/prpc.rs similarity index 100% rename from http-client/src/prpc.rs rename to dstack/http-client/src/prpc.rs diff --git a/iohash/Cargo.toml b/dstack/iohash/Cargo.toml similarity index 100% rename from iohash/Cargo.toml rename to dstack/iohash/Cargo.toml diff --git a/iohash/src/main.rs b/dstack/iohash/src/main.rs similarity index 100% rename from iohash/src/main.rs rename to dstack/iohash/src/main.rs diff --git a/key-provider-build/Cargo.lock b/dstack/key-provider-build/Cargo.lock similarity index 100% rename from key-provider-build/Cargo.lock rename to dstack/key-provider-build/Cargo.lock diff --git a/key-provider-build/Dockerfile.aesmd b/dstack/key-provider-build/Dockerfile.aesmd similarity index 97% rename from key-provider-build/Dockerfile.aesmd rename to dstack/key-provider-build/Dockerfile.aesmd index c5d032507..eb06e5123 100644 --- a/key-provider-build/Dockerfile.aesmd +++ b/dstack/key-provider-build/Dockerfile.aesmd @@ -37,4 +37,4 @@ RUN apt-get update && apt-get install -y \ COPY entrypoint-aesmd.sh /entrypoint.sh RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["/entrypoint.sh"] diff --git a/key-provider-build/Dockerfile.key-provider b/dstack/key-provider-build/Dockerfile.key-provider similarity index 100% rename from key-provider-build/Dockerfile.key-provider rename to dstack/key-provider-build/Dockerfile.key-provider diff --git a/key-provider-build/docker-compose.yaml b/dstack/key-provider-build/docker-compose.yaml similarity index 100% rename from key-provider-build/docker-compose.yaml rename to dstack/key-provider-build/docker-compose.yaml diff --git a/key-provider-build/entrypoint-aesmd.sh b/dstack/key-provider-build/entrypoint-aesmd.sh similarity index 100% rename from key-provider-build/entrypoint-aesmd.sh rename to dstack/key-provider-build/entrypoint-aesmd.sh diff --git a/key-provider-build/entrypoint-key-provider.sh b/dstack/key-provider-build/entrypoint-key-provider.sh similarity index 96% rename from key-provider-build/entrypoint-key-provider.sh rename to dstack/key-provider-build/entrypoint-key-provider.sh index 893e4686b..d96291642 100755 --- a/key-provider-build/entrypoint-key-provider.sh +++ b/dstack/key-provider-build/entrypoint-key-provider.sh @@ -24,4 +24,4 @@ echo "Enclave info:" gramine-sgx-sigstruct-view --output-format json gramine-sealing-key-provider.sig echo "Starting Gramine Sealing Key Provider" -make SGX=1 run-provider \ No newline at end of file +make SGX=1 run-provider diff --git a/key-provider-build/run.sh b/dstack/key-provider-build/run.sh similarity index 93% rename from key-provider-build/run.sh rename to dstack/key-provider-build/run.sh index 42624b8e8..f1b37d167 100755 --- a/key-provider-build/run.sh +++ b/dstack/key-provider-build/run.sh @@ -13,8 +13,8 @@ echo "==========================" echo "Key provider endpoint: https://localhost:3443" echo " - Using shared socket with AESM service" echo " - Socket location: /var/run/aesmd/aesm.socket" -echo +echo echo "Check logs with:" echo " docker compose logs -f aesmd" echo " docker compose logs -f gramine-sealing-key-provider" -echo "==========================" \ No newline at end of file +echo "==========================" diff --git a/key-provider-build/sgx_default_qcnl.conf b/dstack/key-provider-build/sgx_default_qcnl.conf similarity index 99% rename from key-provider-build/sgx_default_qcnl.conf rename to dstack/key-provider-build/sgx_default_qcnl.conf index 38b673839..0f184cdc0 100644 --- a/key-provider-build/sgx_default_qcnl.conf +++ b/dstack/key-provider-build/sgx_default_qcnl.conf @@ -6,4 +6,4 @@ "pck_cache_expire_hours": 168, "verify_collateral_cache_expire_hours": 168, "local_cache_only": false -} \ No newline at end of file +} diff --git a/key-provider-client/Cargo.toml b/dstack/key-provider-client/Cargo.toml similarity index 100% rename from key-provider-client/Cargo.toml rename to dstack/key-provider-client/Cargo.toml diff --git a/key-provider-client/src/host.rs b/dstack/key-provider-client/src/host.rs similarity index 100% rename from key-provider-client/src/host.rs rename to dstack/key-provider-client/src/host.rs diff --git a/key-provider-client/src/lib.rs b/dstack/key-provider-client/src/lib.rs similarity index 100% rename from key-provider-client/src/lib.rs rename to dstack/key-provider-client/src/lib.rs diff --git a/kms/Cargo.toml b/dstack/kms/Cargo.toml similarity index 100% rename from kms/Cargo.toml rename to dstack/kms/Cargo.toml diff --git a/kms/README.md b/dstack/kms/README.md similarity index 99% rename from kms/README.md rename to dstack/kms/README.md index 08d05b1cf..c96033d69 100644 --- a/kms/README.md +++ b/dstack/kms/README.md @@ -124,7 +124,7 @@ When a KMS node receives a key provisioning request, it: #### Vanilla TDX Quote attestation -See [Attestation](../attestation.md) for more details. +See [Intel TDX Attestation](../../docs/attestation-tdx.md) for more details. #### Validating Apps via the KMS Auth Chain diff --git a/kms/auth-simple/.oxlintrc.json b/dstack/kms/auth-eth-bun/.oxlintrc.json similarity index 100% rename from kms/auth-simple/.oxlintrc.json rename to dstack/kms/auth-eth-bun/.oxlintrc.json diff --git a/kms/auth-eth-bun/README.md b/dstack/kms/auth-eth-bun/README.md similarity index 97% rename from kms/auth-eth-bun/README.md rename to dstack/kms/auth-eth-bun/README.md index 02806fe27..9f1725319 100644 --- a/kms/auth-eth-bun/README.md +++ b/dstack/kms/auth-eth-bun/README.md @@ -82,7 +82,7 @@ KMS boot authentication "tcbStatus": "string", "advisoryIds": ["string"], "mrAggregated": "string", - "mrSystem": "string", + "mrSystem": "string", "osImageHash": "string", "appId": "string", "composeHash": "string", @@ -124,4 +124,4 @@ the test suite (`index.test.ts`) validates: - ✅ schema validation using OpenAPI spec - ✅ error handling behavior - ✅ hex encoding/decoding compatibility -- ✅ optional field handling \ No newline at end of file +- ✅ optional field handling diff --git a/kms/auth-eth-bun/bun.lock b/dstack/kms/auth-eth-bun/bun.lock similarity index 100% rename from kms/auth-eth-bun/bun.lock rename to dstack/kms/auth-eth-bun/bun.lock diff --git a/kms/auth-eth-bun/index.test.ts b/dstack/kms/auth-eth-bun/index.test.ts similarity index 100% rename from kms/auth-eth-bun/index.test.ts rename to dstack/kms/auth-eth-bun/index.test.ts diff --git a/kms/auth-eth-bun/index.ts b/dstack/kms/auth-eth-bun/index.ts similarity index 100% rename from kms/auth-eth-bun/index.ts rename to dstack/kms/auth-eth-bun/index.ts diff --git a/kms/auth-eth-bun/openapi.json b/dstack/kms/auth-eth-bun/openapi.json similarity index 100% rename from kms/auth-eth-bun/openapi.json rename to dstack/kms/auth-eth-bun/openapi.json diff --git a/kms/auth-eth-bun/package.json b/dstack/kms/auth-eth-bun/package.json similarity index 100% rename from kms/auth-eth-bun/package.json rename to dstack/kms/auth-eth-bun/package.json diff --git a/kms/auth-simple/vitest.config.ts b/dstack/kms/auth-eth-bun/vitest.config.ts similarity index 100% rename from kms/auth-simple/vitest.config.ts rename to dstack/kms/auth-eth-bun/vitest.config.ts diff --git a/kms/auth-eth/.env.example b/dstack/kms/auth-eth/.env.example similarity index 100% rename from kms/auth-eth/.env.example rename to dstack/kms/auth-eth/.env.example diff --git a/kms/auth-eth/.gitignore b/dstack/kms/auth-eth/.gitignore similarity index 100% rename from kms/auth-eth/.gitignore rename to dstack/kms/auth-eth/.gitignore diff --git a/kms/auth-eth/.openzeppelin/unknown-2035.json b/dstack/kms/auth-eth/.openzeppelin/unknown-2035.json similarity index 100% rename from kms/auth-eth/.openzeppelin/unknown-2035.json rename to dstack/kms/auth-eth/.openzeppelin/unknown-2035.json diff --git a/kms/auth-eth/README.md b/dstack/kms/auth-eth/README.md similarity index 100% rename from kms/auth-eth/README.md rename to dstack/kms/auth-eth/README.md diff --git a/kms/auth-eth/TESTING.md b/dstack/kms/auth-eth/TESTING.md similarity index 100% rename from kms/auth-eth/TESTING.md rename to dstack/kms/auth-eth/TESTING.md diff --git a/kms/auth-eth/contracts/DstackApp.sol b/dstack/kms/auth-eth/contracts/DstackApp.sol similarity index 100% rename from kms/auth-eth/contracts/DstackApp.sol rename to dstack/kms/auth-eth/contracts/DstackApp.sol diff --git a/kms/auth-eth/contracts/DstackKms.sol b/dstack/kms/auth-eth/contracts/DstackKms.sol similarity index 100% rename from kms/auth-eth/contracts/DstackKms.sol rename to dstack/kms/auth-eth/contracts/DstackKms.sol diff --git a/kms/auth-eth/contracts/IAppAuth.sol b/dstack/kms/auth-eth/contracts/IAppAuth.sol similarity index 100% rename from kms/auth-eth/contracts/IAppAuth.sol rename to dstack/kms/auth-eth/contracts/IAppAuth.sol diff --git a/kms/auth-eth/contracts/IAppAuthBasicManagement.sol b/dstack/kms/auth-eth/contracts/IAppAuthBasicManagement.sol similarity index 100% rename from kms/auth-eth/contracts/IAppAuthBasicManagement.sol rename to dstack/kms/auth-eth/contracts/IAppAuthBasicManagement.sol diff --git a/kms/auth-eth/contracts/test-utils/DstackAppV2.sol b/dstack/kms/auth-eth/contracts/test-utils/DstackAppV2.sol similarity index 100% rename from kms/auth-eth/contracts/test-utils/DstackAppV2.sol rename to dstack/kms/auth-eth/contracts/test-utils/DstackAppV2.sol diff --git a/kms/auth-eth/contracts/test-utils/DstackKmsV2.sol b/dstack/kms/auth-eth/contracts/test-utils/DstackKmsV2.sol similarity index 100% rename from kms/auth-eth/contracts/test-utils/DstackKmsV2.sol rename to dstack/kms/auth-eth/contracts/test-utils/DstackKmsV2.sol diff --git a/kms/auth-eth/docs/formal-verification.md b/dstack/kms/auth-eth/docs/formal-verification.md similarity index 100% rename from kms/auth-eth/docs/formal-verification.md rename to dstack/kms/auth-eth/docs/formal-verification.md diff --git a/kms/auth-eth/docs/specification.md b/dstack/kms/auth-eth/docs/specification.md similarity index 100% rename from kms/auth-eth/docs/specification.md rename to dstack/kms/auth-eth/docs/specification.md diff --git a/kms/auth-eth/foundry.toml b/dstack/kms/auth-eth/foundry.toml similarity index 100% rename from kms/auth-eth/foundry.toml rename to dstack/kms/auth-eth/foundry.toml diff --git a/kms/auth-eth/jest.config.js b/dstack/kms/auth-eth/jest.config.js similarity index 100% rename from kms/auth-eth/jest.config.js rename to dstack/kms/auth-eth/jest.config.js diff --git a/kms/auth-eth/lib/forge-std b/dstack/kms/auth-eth/lib/forge-std similarity index 100% rename from kms/auth-eth/lib/forge-std rename to dstack/kms/auth-eth/lib/forge-std diff --git a/kms/auth-eth/lib/openzeppelin-contracts-upgradeable b/dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable similarity index 100% rename from kms/auth-eth/lib/openzeppelin-contracts-upgradeable rename to dstack/kms/auth-eth/lib/openzeppelin-contracts-upgradeable diff --git a/kms/auth-eth/lib/openzeppelin-foundry-upgrades b/dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades similarity index 100% rename from kms/auth-eth/lib/openzeppelin-foundry-upgrades rename to dstack/kms/auth-eth/lib/openzeppelin-foundry-upgrades diff --git a/kms/auth-eth/package-lock.json b/dstack/kms/auth-eth/package-lock.json similarity index 100% rename from kms/auth-eth/package-lock.json rename to dstack/kms/auth-eth/package-lock.json diff --git a/kms/auth-eth/package.json b/dstack/kms/auth-eth/package.json similarity index 100% rename from kms/auth-eth/package.json rename to dstack/kms/auth-eth/package.json diff --git a/kms/auth-eth/run-tests.sh b/dstack/kms/auth-eth/run-tests.sh similarity index 100% rename from kms/auth-eth/run-tests.sh rename to dstack/kms/auth-eth/run-tests.sh diff --git a/kms/auth-eth/script/Deploy.s.sol b/dstack/kms/auth-eth/script/Deploy.s.sol similarity index 100% rename from kms/auth-eth/script/Deploy.s.sol rename to dstack/kms/auth-eth/script/Deploy.s.sol diff --git a/kms/auth-eth/script/Manage.s.sol b/dstack/kms/auth-eth/script/Manage.s.sol similarity index 100% rename from kms/auth-eth/script/Manage.s.sol rename to dstack/kms/auth-eth/script/Manage.s.sol diff --git a/kms/auth-eth/script/Query.s.sol b/dstack/kms/auth-eth/script/Query.s.sol similarity index 100% rename from kms/auth-eth/script/Query.s.sol rename to dstack/kms/auth-eth/script/Query.s.sol diff --git a/kms/auth-eth/script/README.md b/dstack/kms/auth-eth/script/README.md similarity index 100% rename from kms/auth-eth/script/README.md rename to dstack/kms/auth-eth/script/README.md diff --git a/kms/auth-eth/script/Upgrade.s.sol b/dstack/kms/auth-eth/script/Upgrade.s.sol similarity index 100% rename from kms/auth-eth/script/Upgrade.s.sol rename to dstack/kms/auth-eth/script/Upgrade.s.sol diff --git a/kms/auth-eth/scripts/README.md b/dstack/kms/auth-eth/scripts/README.md similarity index 100% rename from kms/auth-eth/scripts/README.md rename to dstack/kms/auth-eth/scripts/README.md diff --git a/kms/auth-eth/scripts/cleanup.sh b/dstack/kms/auth-eth/scripts/cleanup.sh similarity index 100% rename from kms/auth-eth/scripts/cleanup.sh rename to dstack/kms/auth-eth/scripts/cleanup.sh diff --git a/kms/auth-eth/scripts/run-tests.sh b/dstack/kms/auth-eth/scripts/run-tests.sh similarity index 100% rename from kms/auth-eth/scripts/run-tests.sh rename to dstack/kms/auth-eth/scripts/run-tests.sh diff --git a/kms/auth-eth/scripts/setup-local-chain.sh b/dstack/kms/auth-eth/scripts/setup-local-chain.sh similarity index 100% rename from kms/auth-eth/scripts/setup-local-chain.sh rename to dstack/kms/auth-eth/scripts/setup-local-chain.sh diff --git a/kms/auth-eth/scripts/test-all.sh b/dstack/kms/auth-eth/scripts/test-all.sh similarity index 100% rename from kms/auth-eth/scripts/test-all.sh rename to dstack/kms/auth-eth/scripts/test-all.sh diff --git a/kms/auth-eth/slither.config.json b/dstack/kms/auth-eth/slither.config.json similarity index 100% rename from kms/auth-eth/slither.config.json rename to dstack/kms/auth-eth/slither.config.json diff --git a/kms/auth-eth/src/ethereum.ts b/dstack/kms/auth-eth/src/ethereum.ts similarity index 100% rename from kms/auth-eth/src/ethereum.ts rename to dstack/kms/auth-eth/src/ethereum.ts diff --git a/kms/auth-eth/src/main.test.ts b/dstack/kms/auth-eth/src/main.test.ts similarity index 100% rename from kms/auth-eth/src/main.test.ts rename to dstack/kms/auth-eth/src/main.test.ts diff --git a/kms/auth-eth/src/main.ts b/dstack/kms/auth-eth/src/main.ts similarity index 100% rename from kms/auth-eth/src/main.ts rename to dstack/kms/auth-eth/src/main.ts diff --git a/kms/auth-eth/src/server.ts b/dstack/kms/auth-eth/src/server.ts similarity index 100% rename from kms/auth-eth/src/server.ts rename to dstack/kms/auth-eth/src/server.ts diff --git a/kms/auth-eth/src/types.ts b/dstack/kms/auth-eth/src/types.ts similarity index 100% rename from kms/auth-eth/src/types.ts rename to dstack/kms/auth-eth/src/types.ts diff --git a/kms/auth-eth/test/DstackApp.symbolic.t.sol b/dstack/kms/auth-eth/test/DstackApp.symbolic.t.sol similarity index 100% rename from kms/auth-eth/test/DstackApp.symbolic.t.sol rename to dstack/kms/auth-eth/test/DstackApp.symbolic.t.sol diff --git a/kms/auth-eth/test/DstackApp.t.sol b/dstack/kms/auth-eth/test/DstackApp.t.sol similarity index 100% rename from kms/auth-eth/test/DstackApp.t.sol rename to dstack/kms/auth-eth/test/DstackApp.t.sol diff --git a/kms/auth-eth/test/DstackKms.symbolic.t.sol b/dstack/kms/auth-eth/test/DstackKms.symbolic.t.sol similarity index 100% rename from kms/auth-eth/test/DstackKms.symbolic.t.sol rename to dstack/kms/auth-eth/test/DstackKms.symbolic.t.sol diff --git a/kms/auth-eth/test/DstackKms.t.sol b/dstack/kms/auth-eth/test/DstackKms.t.sol similarity index 100% rename from kms/auth-eth/test/DstackKms.t.sol rename to dstack/kms/auth-eth/test/DstackKms.t.sol diff --git a/kms/auth-eth/test/UpgradesWithPlugin.t.sol b/dstack/kms/auth-eth/test/UpgradesWithPlugin.t.sol similarity index 100% rename from kms/auth-eth/test/UpgradesWithPlugin.t.sol rename to dstack/kms/auth-eth/test/UpgradesWithPlugin.t.sol diff --git a/kms/auth-eth/tsconfig.json b/dstack/kms/auth-eth/tsconfig.json similarity index 100% rename from kms/auth-eth/tsconfig.json rename to dstack/kms/auth-eth/tsconfig.json diff --git a/kms/auth-eth-bun/.oxlintrc.json b/dstack/kms/auth-mock/.oxlintrc.json similarity index 99% rename from kms/auth-eth-bun/.oxlintrc.json rename to dstack/kms/auth-mock/.oxlintrc.json index 1b685f410..f7d7b8e35 100644 --- a/kms/auth-eth-bun/.oxlintrc.json +++ b/dstack/kms/auth-mock/.oxlintrc.json @@ -24,4 +24,4 @@ "*.d.ts", "*.test.ts" ] -} \ No newline at end of file +} diff --git a/kms/auth-mock/Dockerfile b/dstack/kms/auth-mock/Dockerfile similarity index 90% rename from kms/auth-mock/Dockerfile rename to dstack/kms/auth-mock/Dockerfile index 095ff1b29..3bb12a25d 100644 --- a/kms/auth-mock/Dockerfile +++ b/dstack/kms/auth-mock/Dockerfile @@ -12,6 +12,6 @@ RUN apk add --no-cache git RUN git clone --branch ${DSTACK_BRANCH} https://github.com/Dstack-TEE/dstack.git && \ cd dstack && \ git checkout ${DSTACK_REV} -WORKDIR /app/dstack/kms/auth-mock +WORKDIR /app/dstack/dstack/kms/auth-mock RUN bun install --frozen-lockfile CMD ["bun", "index.ts"] diff --git a/kms/auth-mock/README.md b/dstack/kms/auth-mock/README.md similarity index 99% rename from kms/auth-mock/README.md rename to dstack/kms/auth-mock/README.md index 1ff28747d..c26ed0e19 100644 --- a/kms/auth-mock/README.md +++ b/dstack/kms/auth-mock/README.md @@ -93,7 +93,7 @@ identical to the real backend: ```json { "mrAggregated": "string", - "osImageHash": "string", + "osImageHash": "string", "appId": "string", "composeHash": "string", "instanceId": "string", @@ -165,4 +165,4 @@ to switch from mock to real backend: 3. deploy smart contracts 4. update environment variables -the API remains identical, so no client code changes are needed. \ No newline at end of file +the API remains identical, so no client code changes are needed. diff --git a/kms/auth-mock/bun.lock b/dstack/kms/auth-mock/bun.lock similarity index 100% rename from kms/auth-mock/bun.lock rename to dstack/kms/auth-mock/bun.lock diff --git a/kms/auth-mock/index.test.ts b/dstack/kms/auth-mock/index.test.ts similarity index 99% rename from kms/auth-mock/index.test.ts rename to dstack/kms/auth-mock/index.test.ts index 177fa1b44..98521ec61 100644 --- a/kms/auth-mock/index.test.ts +++ b/dstack/kms/auth-mock/index.test.ts @@ -12,7 +12,7 @@ beforeAll(async () => { // Set environment variables for testing process.env.KMS_CONTRACT_ADDR = '0xmockcontract1234567890123456789012345678'; process.env.PORT = '3002'; - + // Import the app const indexModule = await import('./index.ts'); appFetch = indexModule.default.fetch; @@ -44,11 +44,11 @@ describe('Mock Backend Tests', () => { expect(data.gatewayAppId).toBe(process.env.MOCK_GATEWAY_APP_ID || '0xmockgateway1234567890123456789012345678'); expect(data.chainId).toBe(parseInt(process.env.MOCK_CHAIN_ID || '1337')); expect(data.appImplementation).toBe(process.env.MOCK_APP_IMPLEMENTATION || '0xmockapp9876543210987654321098765432109'); - + // Verify response structure matches OpenAPI spec const systemInfoSchema = openApiSpec.components.schemas.MockSystemInfo; const requiredFields = systemInfoSchema.required; - + requiredFields.forEach(field => { expect(data).toHaveProperty(field); }); @@ -67,7 +67,7 @@ describe('Mock Backend Tests', () => { it('should always return success for app auth', async () => { const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); - + const response = await appFetch(new Request('http://localhost:3002/bootAuth/app', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -93,7 +93,7 @@ describe('Mock Backend Tests', () => { // Verify response matches OpenAPI spec const bootResponseSchema = openApiSpec.components.schemas.BootResponse; const requiredFields = bootResponseSchema.required; - + requiredFields.forEach(field => { expect(data).toHaveProperty(field); }); @@ -172,7 +172,7 @@ describe('Mock Backend Tests', () => { it('should always return success for KMS auth', async () => { const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); - + const response = await appFetch(new Request('http://localhost:3002/bootAuth/kms', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -225,7 +225,7 @@ describe('Mock Backend Tests', () => { it('should handle "Test backend error" appropriately', async () => { const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); - + // This shouldn't actually throw an error in mock backend, but test the error handling path const response = await appFetch(new Request('http://localhost:3002/bootAuth/kms', { method: 'POST', @@ -240,7 +240,7 @@ describe('Mock Backend Tests', () => { // Verify console.error was not called (no errors in mock) expect(consoleSpy).not.toHaveBeenCalled(); - + consoleSpy.mockRestore(); }); }); @@ -249,7 +249,7 @@ describe('Mock Backend Tests', () => { describe('API Schema Compatibility', () => { it('should match BootInfo schema requirements', () => { const bootInfoSchema = openApiSpec.components.schemas.BootInfo; - + // Required fields should match original schema expect(bootInfoSchema.required).toEqual([ 'mrAggregated', @@ -268,7 +268,7 @@ describe('API Schema Compatibility', () => { it('should match BootResponse schema requirements', () => { const bootResponseSchema = openApiSpec.components.schemas.BootResponse; - + expect(bootResponseSchema.required).toEqual([ 'isAllowed', 'reason', @@ -282,7 +282,7 @@ describe('API Schema Compatibility', () => { it('should match MockSystemInfo schema requirements', () => { const systemInfoSchema = openApiSpec.components.schemas.MockSystemInfo; - + expect(systemInfoSchema.required).toEqual([ 'status', 'kmsContractAddr', @@ -333,4 +333,4 @@ describe('Mock Behavior Verification', () => { expect(appData.reason).toBe('mock app always allowed'); expect(kmsData.reason).toBe('mock KMS always allowed'); }); -}); \ No newline at end of file +}); diff --git a/kms/auth-mock/index.ts b/dstack/kms/auth-mock/index.ts similarity index 100% rename from kms/auth-mock/index.ts rename to dstack/kms/auth-mock/index.ts diff --git a/kms/auth-mock/openapi.json b/dstack/kms/auth-mock/openapi.json similarity index 98% rename from kms/auth-mock/openapi.json rename to dstack/kms/auth-mock/openapi.json index 74e34702b..1689308d0 100644 --- a/kms/auth-mock/openapi.json +++ b/dstack/kms/auth-mock/openapi.json @@ -106,7 +106,7 @@ "type": "object", "required": [ "mrAggregated", - "osImageHash", + "osImageHash", "appId", "composeHash", "instanceId", @@ -118,7 +118,7 @@ "description": "Aggregated MR measurement" }, "osImageHash": { - "type": "string", + "type": "string", "description": "OS Image hash" }, "appId": { @@ -158,7 +158,7 @@ "type": "object", "required": [ "isAllowed", - "reason", + "reason", "gatewayAppId" ], "properties": { @@ -202,7 +202,7 @@ "example": "0xmockcontract1234567890123456789012345678" }, "gatewayAppId": { - "type": "string", + "type": "string", "description": "Mock gateway application ID", "example": "0xmockgateway1234567890123456789012345678" }, @@ -247,4 +247,4 @@ } } } -} \ No newline at end of file +} diff --git a/kms/auth-mock/package.json b/dstack/kms/auth-mock/package.json similarity index 100% rename from kms/auth-mock/package.json rename to dstack/kms/auth-mock/package.json diff --git a/kms/auth-eth-bun/vitest.config.ts b/dstack/kms/auth-mock/vitest.config.ts similarity index 98% rename from kms/auth-eth-bun/vitest.config.ts rename to dstack/kms/auth-mock/vitest.config.ts index 990992495..17597daca 100644 --- a/kms/auth-eth-bun/vitest.config.ts +++ b/dstack/kms/auth-mock/vitest.config.ts @@ -9,4 +9,4 @@ export default defineConfig({ globals: true, environment: 'node', }, -}); \ No newline at end of file +}); diff --git a/kms/auth-mock/.oxlintrc.json b/dstack/kms/auth-simple/.oxlintrc.json similarity index 99% rename from kms/auth-mock/.oxlintrc.json rename to dstack/kms/auth-simple/.oxlintrc.json index 1b685f410..f7d7b8e35 100644 --- a/kms/auth-mock/.oxlintrc.json +++ b/dstack/kms/auth-simple/.oxlintrc.json @@ -24,4 +24,4 @@ "*.d.ts", "*.test.ts" ] -} \ No newline at end of file +} diff --git a/kms/auth-simple/README.md b/dstack/kms/auth-simple/README.md similarity index 98% rename from kms/auth-simple/README.md rename to dstack/kms/auth-simple/README.md index 58360895f..6458d5a50 100644 --- a/kms/auth-simple/README.md +++ b/dstack/kms/auth-simple/README.md @@ -212,4 +212,4 @@ bun run test:run - [auth-eth](../auth-eth/) - On-chain governance auth server - [auth-mock](../auth-mock/) - Development/testing auth server (always allows) -- [Deployment Guide](../../docs/deployment.md) - Full deployment instructions +- [Deployment Guide](../../../docs/deployment.md) - Full deployment instructions diff --git a/kms/auth-simple/auth-config.example.json b/dstack/kms/auth-simple/auth-config.example.json similarity index 100% rename from kms/auth-simple/auth-config.example.json rename to dstack/kms/auth-simple/auth-config.example.json diff --git a/kms/auth-simple/bun.lock b/dstack/kms/auth-simple/bun.lock similarity index 100% rename from kms/auth-simple/bun.lock rename to dstack/kms/auth-simple/bun.lock diff --git a/kms/auth-simple/index.test.ts b/dstack/kms/auth-simple/index.test.ts similarity index 100% rename from kms/auth-simple/index.test.ts rename to dstack/kms/auth-simple/index.test.ts diff --git a/kms/auth-simple/index.ts b/dstack/kms/auth-simple/index.ts similarity index 100% rename from kms/auth-simple/index.ts rename to dstack/kms/auth-simple/index.ts diff --git a/kms/auth-simple/package.json b/dstack/kms/auth-simple/package.json similarity index 100% rename from kms/auth-simple/package.json rename to dstack/kms/auth-simple/package.json diff --git a/kms/auth-mock/vitest.config.ts b/dstack/kms/auth-simple/vitest.config.ts similarity index 98% rename from kms/auth-mock/vitest.config.ts rename to dstack/kms/auth-simple/vitest.config.ts index 990992495..17597daca 100644 --- a/kms/auth-mock/vitest.config.ts +++ b/dstack/kms/auth-simple/vitest.config.ts @@ -9,4 +9,4 @@ export default defineConfig({ globals: true, environment: 'node', }, -}); \ No newline at end of file +}); diff --git a/kms/dstack-app/.gitignore b/dstack/kms/dstack-app/.gitignore similarity index 100% rename from kms/dstack-app/.gitignore rename to dstack/kms/dstack-app/.gitignore diff --git a/kms/dstack-app/builder/Dockerfile b/dstack/kms/dstack-app/builder/Dockerfile similarity index 89% rename from kms/dstack-app/builder/Dockerfile rename to dstack/kms/dstack-app/builder/Dockerfile index f924d0e02..038324c3a 100644 --- a/kms/dstack-app/builder/Dockerfile +++ b/dstack/kms/dstack-app/builder/Dockerfile @@ -19,11 +19,11 @@ RUN apt-get update && \ libprotobuf-dev \ clang \ libclang-dev -RUN git clone ${DSTACK_SRC_URL} dstack && \ - cd dstack && \ +RUN git clone ${DSTACK_SRC_URL} repo && \ + cd repo && \ git checkout ${DSTACK_REV} RUN rustup target add x86_64-unknown-linux-musl -RUN cd dstack && cargo build --release -p dstack-kms --target x86_64-unknown-linux-musl +RUN cd repo/dstack && cargo build --release -p dstack-kms --target x86_64-unknown-linux-musl RUN echo "${DSTACK_REV}" > /build/.GIT_REV FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe @@ -60,6 +60,6 @@ RUN git clone https://github.com/kvinwang/qemu-tdx.git --depth 1 --branch dstack install -m 644 pc-bios/kvmvapic.bin /usr/local/share/qemu/ && \ install -m 644 pc-bios/linuxboot_dma.bin /usr/local/share/qemu/ && \ cd .. && rm -rf qemu-tdx -COPY --from=kms-builder /build/dstack/target/x86_64-unknown-linux-musl/release/dstack-kms /usr/local/bin/dstack-kms +COPY --from=kms-builder /build/repo/dstack/target/x86_64-unknown-linux-musl/release/dstack-kms /usr/local/bin/dstack-kms COPY --from=kms-builder /build/.GIT_REV /etc/ CMD ["dstack-kms"] diff --git a/kms/dstack-app/builder/README.md b/dstack/kms/dstack-app/builder/README.md similarity index 100% rename from kms/dstack-app/builder/README.md rename to dstack/kms/dstack-app/builder/README.md diff --git a/kms/dstack-app/builder/build-image.sh b/dstack/kms/dstack-app/builder/build-image.sh similarity index 94% rename from kms/dstack-app/builder/build-image.sh rename to dstack/kms/dstack-app/builder/build-image.sh index 73be520e9..b92c9adcb 100755 --- a/kms/dstack-app/builder/build-image.sh +++ b/dstack/kms/dstack-app/builder/build-image.sh @@ -12,7 +12,7 @@ CONTEXT_DIR="$SCRIPT_DIR" SHARED_DIR="$SCRIPT_DIR/shared" DOCKERFILE="$SCRIPT_DIR/Dockerfile" -source "$REPO_ROOT/build/shared/build-lib.sh" +source "$REPO_ROOT/dstack/build/shared/build-lib.sh" NAME=${1:-} if [ -z "$NAME" ]; then diff --git a/kms/dstack-app/builder/shared/builder-pinned-packages.txt b/dstack/kms/dstack-app/builder/shared/builder-pinned-packages.txt similarity index 100% rename from kms/dstack-app/builder/shared/builder-pinned-packages.txt rename to dstack/kms/dstack-app/builder/shared/builder-pinned-packages.txt diff --git a/kms/dstack-app/builder/shared/qemu-pinned-packages.txt b/dstack/kms/dstack-app/builder/shared/qemu-pinned-packages.txt similarity index 100% rename from kms/dstack-app/builder/shared/qemu-pinned-packages.txt rename to dstack/kms/dstack-app/builder/shared/qemu-pinned-packages.txt diff --git a/kms/dstack-app/compose-dev.yaml b/dstack/kms/dstack-app/compose-dev.yaml similarity index 97% rename from kms/dstack-app/compose-dev.yaml rename to dstack/kms/dstack-app/compose-dev.yaml index 755f00e36..c216496ef 100644 --- a/kms/dstack-app/compose-dev.yaml +++ b/dstack/kms/dstack-app/compose-dev.yaml @@ -15,7 +15,7 @@ services: RUN git clone ${GIT_REPOSITORY} && \ cd dstack && \ git checkout ${GIT_REV} - WORKDIR /app/dstack/kms/auth-eth + WORKDIR /app/dstack/dstack/kms/auth-eth RUN npm install RUN npx tsc --project tsconfig.json CMD node dist/src/main.js diff --git a/kms/dstack-app/compose-simple.yaml b/dstack/kms/dstack-app/compose-simple.yaml similarity index 100% rename from kms/dstack-app/compose-simple.yaml rename to dstack/kms/dstack-app/compose-simple.yaml diff --git a/kms/dstack-app/deploy-simple.sh b/dstack/kms/dstack-app/deploy-simple.sh similarity index 89% rename from kms/dstack-app/deploy-simple.sh rename to dstack/kms/dstack-app/deploy-simple.sh index f027f24aa..c24b37715 100755 --- a/kms/dstack-app/deploy-simple.sh +++ b/dstack/kms/dstack-app/deploy-simple.sh @@ -13,6 +13,7 @@ set -e if [ -f ".env.simple" ]; then echo "Loading environment variables from .env.simple file..." set -a + # shellcheck source=/dev/null source .env.simple set +a else @@ -70,11 +71,13 @@ for var in "${required_env_vars[@]}"; do fi done -CLI="../../vmm/src/vmm-cli.py --url $VMM_RPC" +CLI=(../../vmm/src/vmm-cli.py --url "$VMM_RPC") COMPOSE_TMP=$(mktemp) -ADMIN_TOKEN_HASH=$(echo -n $ADMIN_TOKEN | sha256sum | cut -d' ' -f1) +# Used indirectly by subvar. +# shellcheck disable=SC2034 +ADMIN_TOKEN_HASH=$(printf '%s' "$ADMIN_TOKEN" | sha256sum | cut -d' ' -f1) cp compose-simple.yaml "$COMPOSE_TMP" @@ -99,7 +102,7 @@ echo " OS_IMAGE: $OS_IMAGE" echo "" if [ -t 0 ]; then - read -p "Continue? [y/N] " -n 1 -r + read -r -p "Continue? [y/N] " -n 1 echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then @@ -108,7 +111,7 @@ if [ -t 0 ]; then fi fi -$CLI compose \ +"${CLI[@]}" compose \ --docker-compose "$COMPOSE_TMP" \ --name kms \ --local-key-provider \ @@ -122,12 +125,12 @@ rm "$COMPOSE_TMP" echo "Deploying KMS to dstack-vmm..." -$CLI deploy \ +"${CLI[@]}" deploy \ --name kms \ --compose .app-compose.json \ - --image $OS_IMAGE \ - --port tcp:$KMS_RPC_ADDR:8000 \ - --port tcp:$GUEST_AGENT_ADDR:8090 \ + --image "$OS_IMAGE" \ + --port "tcp:$KMS_RPC_ADDR:8000" \ + --port "tcp:$GUEST_AGENT_ADDR:8090" \ --vcpu 8 \ --memory 8G \ --disk 50G diff --git a/kms/dstack-app/deploy-to-vmm.sh b/dstack/kms/dstack-app/deploy-to-vmm.sh similarity index 100% rename from kms/dstack-app/deploy-to-vmm.sh rename to dstack/kms/dstack-app/deploy-to-vmm.sh diff --git a/kms/dstack-app/docker-compose.yaml b/dstack/kms/dstack-app/docker-compose.yaml similarity index 100% rename from kms/dstack-app/docker-compose.yaml rename to dstack/kms/dstack-app/docker-compose.yaml diff --git a/kms/dstack-app/entrypoint.sh b/dstack/kms/dstack-app/entrypoint.sh similarity index 100% rename from kms/dstack-app/entrypoint.sh rename to dstack/kms/dstack-app/entrypoint.sh diff --git a/kms/kms.toml b/dstack/kms/kms.toml similarity index 100% rename from kms/kms.toml rename to dstack/kms/kms.toml diff --git a/kms/rpc/Cargo.toml b/dstack/kms/rpc/Cargo.toml similarity index 100% rename from kms/rpc/Cargo.toml rename to dstack/kms/rpc/Cargo.toml diff --git a/kms/rpc/build.rs b/dstack/kms/rpc/build.rs similarity index 100% rename from kms/rpc/build.rs rename to dstack/kms/rpc/build.rs diff --git a/kms/rpc/proto/kms_rpc.proto b/dstack/kms/rpc/proto/kms_rpc.proto similarity index 100% rename from kms/rpc/proto/kms_rpc.proto rename to dstack/kms/rpc/proto/kms_rpc.proto diff --git a/kms/rpc/src/.gitignore b/dstack/kms/rpc/src/.gitignore similarity index 100% rename from kms/rpc/src/.gitignore rename to dstack/kms/rpc/src/.gitignore diff --git a/kms/rpc/src/generated.rs b/dstack/kms/rpc/src/generated.rs similarity index 100% rename from kms/rpc/src/generated.rs rename to dstack/kms/rpc/src/generated.rs diff --git a/kms/rpc/src/lib.rs b/dstack/kms/rpc/src/lib.rs similarity index 100% rename from kms/rpc/src/lib.rs rename to dstack/kms/rpc/src/lib.rs diff --git a/kms/src/config.rs b/dstack/kms/src/config.rs similarity index 100% rename from kms/src/config.rs rename to dstack/kms/src/config.rs diff --git a/kms/src/crypto.rs b/dstack/kms/src/crypto.rs similarity index 100% rename from kms/src/crypto.rs rename to dstack/kms/src/crypto.rs diff --git a/kms/src/ct_log.rs b/dstack/kms/src/ct_log.rs similarity index 100% rename from kms/src/ct_log.rs rename to dstack/kms/src/ct_log.rs diff --git a/kms/src/main.rs b/dstack/kms/src/main.rs similarity index 100% rename from kms/src/main.rs rename to dstack/kms/src/main.rs diff --git a/kms/src/main_service.rs b/dstack/kms/src/main_service.rs similarity index 100% rename from kms/src/main_service.rs rename to dstack/kms/src/main_service.rs diff --git a/kms/src/main_service/amd_attest.rs b/dstack/kms/src/main_service/amd_attest.rs similarity index 100% rename from kms/src/main_service/amd_attest.rs rename to dstack/kms/src/main_service/amd_attest.rs diff --git a/kms/src/main_service/upgrade_authority.rs b/dstack/kms/src/main_service/upgrade_authority.rs similarity index 100% rename from kms/src/main_service/upgrade_authority.rs rename to dstack/kms/src/main_service/upgrade_authority.rs diff --git a/kms/src/onboard_service.rs b/dstack/kms/src/onboard_service.rs similarity index 100% rename from kms/src/onboard_service.rs rename to dstack/kms/src/onboard_service.rs diff --git a/kms/src/www/onboard.html b/dstack/kms/src/www/onboard.html similarity index 100% rename from kms/src/www/onboard.html rename to dstack/kms/src/www/onboard.html diff --git a/load_config/Cargo.toml b/dstack/load_config/Cargo.toml similarity index 100% rename from load_config/Cargo.toml rename to dstack/load_config/Cargo.toml diff --git a/load_config/src/lib.rs b/dstack/load_config/src/lib.rs similarity index 100% rename from load_config/src/lib.rs rename to dstack/load_config/src/lib.rs diff --git a/lspci/Cargo.toml b/dstack/lspci/Cargo.toml similarity index 100% rename from lspci/Cargo.toml rename to dstack/lspci/Cargo.toml diff --git a/lspci/src/lib.rs b/dstack/lspci/src/lib.rs similarity index 100% rename from lspci/src/lib.rs rename to dstack/lspci/src/lib.rs diff --git a/lspci/src/snapshots/lspci__lspci.snap b/dstack/lspci/src/snapshots/lspci__lspci.snap similarity index 100% rename from lspci/src/snapshots/lspci__lspci.snap rename to dstack/lspci/src/snapshots/lspci__lspci.snap diff --git a/no_std_check/Cargo.toml b/dstack/no_std_check/Cargo.toml similarity index 100% rename from no_std_check/Cargo.toml rename to dstack/no_std_check/Cargo.toml diff --git a/no_std_check/src/lib.rs b/dstack/no_std_check/src/lib.rs similarity index 100% rename from no_std_check/src/lib.rs rename to dstack/no_std_check/src/lib.rs diff --git a/nsm-attest/Cargo.toml b/dstack/nsm-attest/Cargo.toml similarity index 100% rename from nsm-attest/Cargo.toml rename to dstack/nsm-attest/Cargo.toml diff --git a/nsm-attest/src/lib.rs b/dstack/nsm-attest/src/lib.rs similarity index 100% rename from nsm-attest/src/lib.rs rename to dstack/nsm-attest/src/lib.rs diff --git a/nsm-attest/src/types.rs b/dstack/nsm-attest/src/types.rs similarity index 100% rename from nsm-attest/src/types.rs rename to dstack/nsm-attest/src/types.rs diff --git a/nsm-attest/tests/attestation_test.rs b/dstack/nsm-attest/tests/attestation_test.rs similarity index 100% rename from nsm-attest/tests/attestation_test.rs rename to dstack/nsm-attest/tests/attestation_test.rs diff --git a/nsm-attest/tests/nitro_attestation.bin b/dstack/nsm-attest/tests/nitro_attestation.bin similarity index 100% rename from nsm-attest/tests/nitro_attestation.bin rename to dstack/nsm-attest/tests/nitro_attestation.bin diff --git a/nsm-qvl/Cargo.toml b/dstack/nsm-qvl/Cargo.toml similarity index 100% rename from nsm-qvl/Cargo.toml rename to dstack/nsm-qvl/Cargo.toml diff --git a/nsm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem b/dstack/nsm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem similarity index 100% rename from nsm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem rename to dstack/nsm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem diff --git a/nsm-qvl/src/collateral.rs b/dstack/nsm-qvl/src/collateral.rs similarity index 100% rename from nsm-qvl/src/collateral.rs rename to dstack/nsm-qvl/src/collateral.rs diff --git a/nsm-qvl/src/lib.rs b/dstack/nsm-qvl/src/lib.rs similarity index 100% rename from nsm-qvl/src/lib.rs rename to dstack/nsm-qvl/src/lib.rs diff --git a/nsm-qvl/src/verify.rs b/dstack/nsm-qvl/src/verify.rs similarity index 100% rename from nsm-qvl/src/verify.rs rename to dstack/nsm-qvl/src/verify.rs diff --git a/nsm-qvl/tests/nitro_attestation.bin b/dstack/nsm-qvl/tests/nitro_attestation.bin similarity index 100% rename from nsm-qvl/tests/nitro_attestation.bin rename to dstack/nsm-qvl/tests/nitro_attestation.bin diff --git a/nsm-qvl/tests/verify_test.rs b/dstack/nsm-qvl/tests/verify_test.rs similarity index 100% rename from nsm-qvl/tests/verify_test.rs rename to dstack/nsm-qvl/tests/verify_test.rs diff --git a/port-forward/Cargo.toml b/dstack/port-forward/Cargo.toml similarity index 100% rename from port-forward/Cargo.toml rename to dstack/port-forward/Cargo.toml diff --git a/port-forward/src/lib.rs b/dstack/port-forward/src/lib.rs similarity index 100% rename from port-forward/src/lib.rs rename to dstack/port-forward/src/lib.rs diff --git a/port-forward/src/tcp.rs b/dstack/port-forward/src/tcp.rs similarity index 100% rename from port-forward/src/tcp.rs rename to dstack/port-forward/src/tcp.rs diff --git a/port-forward/src/udp.rs b/dstack/port-forward/src/udp.rs similarity index 100% rename from port-forward/src/udp.rs rename to dstack/port-forward/src/udp.rs diff --git a/python/.gitignore b/dstack/python/.gitignore similarity index 100% rename from python/.gitignore rename to dstack/python/.gitignore diff --git a/python/ct_monitor/ct_monitor.py b/dstack/python/ct_monitor/ct_monitor.py similarity index 100% rename from python/ct_monitor/ct_monitor.py rename to dstack/python/ct_monitor/ct_monitor.py diff --git a/python/ct_monitor/pyproject.toml b/dstack/python/ct_monitor/pyproject.toml similarity index 100% rename from python/ct_monitor/pyproject.toml rename to dstack/python/ct_monitor/pyproject.toml diff --git a/ra-rpc/Cargo.toml b/dstack/ra-rpc/Cargo.toml similarity index 100% rename from ra-rpc/Cargo.toml rename to dstack/ra-rpc/Cargo.toml diff --git a/ra-rpc/prpc-openapi.md b/dstack/ra-rpc/prpc-openapi.md similarity index 100% rename from ra-rpc/prpc-openapi.md rename to dstack/ra-rpc/prpc-openapi.md diff --git a/ra-rpc/src/client.rs b/dstack/ra-rpc/src/client.rs similarity index 100% rename from ra-rpc/src/client.rs rename to dstack/ra-rpc/src/client.rs diff --git a/ra-rpc/src/lib.rs b/dstack/ra-rpc/src/lib.rs similarity index 100% rename from ra-rpc/src/lib.rs rename to dstack/ra-rpc/src/lib.rs diff --git a/ra-rpc/src/openapi.rs b/dstack/ra-rpc/src/openapi.rs similarity index 100% rename from ra-rpc/src/openapi.rs rename to dstack/ra-rpc/src/openapi.rs diff --git a/ra-rpc/src/rocket_helper.rs b/dstack/ra-rpc/src/rocket_helper.rs similarity index 100% rename from ra-rpc/src/rocket_helper.rs rename to dstack/ra-rpc/src/rocket_helper.rs diff --git a/ra-tls/Cargo.toml b/dstack/ra-tls/Cargo.toml similarity index 100% rename from ra-tls/Cargo.toml rename to dstack/ra-tls/Cargo.toml diff --git a/ra-tls/assets/tdx_quote b/dstack/ra-tls/assets/tdx_quote similarity index 100% rename from ra-tls/assets/tdx_quote rename to dstack/ra-tls/assets/tdx_quote diff --git a/ra-tls/src/attestation.rs b/dstack/ra-tls/src/attestation.rs similarity index 100% rename from ra-tls/src/attestation.rs rename to dstack/ra-tls/src/attestation.rs diff --git a/ra-tls/src/cert.rs b/dstack/ra-tls/src/cert.rs similarity index 100% rename from ra-tls/src/cert.rs rename to dstack/ra-tls/src/cert.rs diff --git a/ra-tls/src/kdf.rs b/dstack/ra-tls/src/kdf.rs similarity index 100% rename from ra-tls/src/kdf.rs rename to dstack/ra-tls/src/kdf.rs diff --git a/ra-tls/src/lib.rs b/dstack/ra-tls/src/lib.rs similarity index 100% rename from ra-tls/src/lib.rs rename to dstack/ra-tls/src/lib.rs diff --git a/ra-tls/src/oids.rs b/dstack/ra-tls/src/oids.rs similarity index 100% rename from ra-tls/src/oids.rs rename to dstack/ra-tls/src/oids.rs diff --git a/ra-tls/src/traits.rs b/dstack/ra-tls/src/traits.rs similarity index 100% rename from ra-tls/src/traits.rs rename to dstack/ra-tls/src/traits.rs diff --git a/rocket-vsock-listener/Cargo.toml b/dstack/rocket-vsock-listener/Cargo.toml similarity index 100% rename from rocket-vsock-listener/Cargo.toml rename to dstack/rocket-vsock-listener/Cargo.toml diff --git a/rocket-vsock-listener/src/lib.rs b/dstack/rocket-vsock-listener/src/lib.rs similarity index 100% rename from rocket-vsock-listener/src/lib.rs rename to dstack/rocket-vsock-listener/src/lib.rs diff --git a/run-tests.sh b/dstack/run-tests.sh similarity index 77% rename from run-tests.sh rename to dstack/run-tests.sh index 59aa0a5fd..4fb8dbd94 100755 --- a/run-tests.sh +++ b/dstack/run-tests.sh @@ -6,11 +6,14 @@ set -Eeuo pipefail -ROOT_DIR="$(pwd -P)" -SIMULATOR_DIR="$ROOT_DIR/sdk/simulator" +CORE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +REPO_ROOT="$(cd "$CORE_DIR/.." && pwd -P)" +SIMULATOR_DIR="$REPO_ROOT/sdk/simulator" SIMULATOR_LOG="$SIMULATOR_DIR/dstack-simulator.log" DSTACK_SOCKET="$SIMULATOR_DIR/dstack.sock" TAPPD_SOCKET="$SIMULATOR_DIR/tappd.sock" +GUEST_SOCKET="$SIMULATOR_DIR/guest.sock" +EXTERNAL_SOCKET="$SIMULATOR_DIR/external.sock" SIMULATOR_PID="" cleanup() { @@ -18,6 +21,7 @@ cleanup() { kill "$SIMULATOR_PID" 2>/dev/null || true wait "$SIMULATOR_PID" 2>/dev/null || true fi + rm -f "$DSTACK_SOCKET" "$TAPPD_SOCKET" "$GUEST_SOCKET" "$EXTERNAL_SOCKET" } print_simulator_logs() { @@ -51,7 +55,12 @@ wait_for_socket() { trap 'print_simulator_logs' ERR trap cleanup EXIT INT TERM -rm -f "$DSTACK_SOCKET" "$TAPPD_SOCKET" "$SIMULATOR_LOG" +rm -f \ + "$DSTACK_SOCKET" \ + "$TAPPD_SOCKET" \ + "$GUEST_SOCKET" \ + "$EXTERNAL_SOCKET" \ + "$SIMULATOR_LOG" ( cd "$SIMULATOR_DIR" ./build.sh @@ -73,4 +82,4 @@ export TAPPD_SIMULATOR_ENDPOINT="$TAPPD_SOCKET" echo "DSTACK_SIMULATOR_ENDPOINT: $DSTACK_SIMULATOR_ENDPOINT" echo "TAPPD_SIMULATOR_ENDPOINT: $TAPPD_SIMULATOR_ENDPOINT" -cargo test --all-features -- --show-output +(cd "$CORE_DIR" && cargo test --all-features -- --show-output) diff --git a/scripts/bin/dstack-cloud b/dstack/scripts/bin/dstack-cloud similarity index 98% rename from scripts/bin/dstack-cloud rename to dstack/scripts/bin/dstack-cloud index 019ecda04..6d66c4b70 100755 --- a/scripts/bin/dstack-cloud +++ b/dstack/scripts/bin/dstack-cloud @@ -33,6 +33,7 @@ import hashlib import json import logging import os +import re import subprocess import sys import tempfile @@ -79,6 +80,27 @@ GLOBAL_CONFIG_PATH = os.path.expanduser( os.environ.get("DSTACK_CLOUD_CONFIG") or "~/.config/dstack-cloud/config.json" ) DEFAULT_OS_IMAGE = "dstack-0.6.0" +MONOREPO_GUEST_OS_MIN_VERSION = (0, 6, 0) + + +def guest_os_release_url(os_image: str, version: str) -> str: + """Return the UKI release URL for a versioned guest image.""" + match = re.fullmatch( + r'(\d+)\.(\d+)\.(\d+)(?:[-.][0-9A-Za-z][0-9A-Za-z.-]*)?', + version, + ) + if not match: + raise ValueError(f"Invalid guest OS version: {version}") + core = tuple(int(part) for part in match.groups()) + if core < MONOREPO_GUEST_OS_MIN_VERSION: + return ( + "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/Dstack-TEE/meta-dstack/releases/download/" + f"v{version}/{os_image}-uki.tar.gz" + ) + return ( + "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/Dstack-TEE/dstack/releases/download/" + f"guest-os-v{version}/{os_image}-uki.tar.gz" + ) @dataclass @@ -1020,16 +1042,18 @@ class CloudDeploymentManager: os_image = url_filename download_tar = target_dir / url_filename else: - # Extract version from os_image (e.g., dstack-nvidia-0.6.0 -> 0.6.0) - # Version is the last component after the last hyphen followed by digits - import re - version_match = re.search(r'-(\d+\.\d+\.\d+)$', os_image) + # Extract the release version from the unified image name (or a + # legacy flavor name), including an optional prerelease suffix. + version_match = re.search( + r'-(\d+\.\d+\.\d+(?:[-.][0-9A-Za-z][0-9A-Za-z.-]*)?)$', + os_image, + ) if not version_match: logger.error(f"Could not extract version from image name: {os_image}") - logger.error("Expected format: dstack-- (e.g., dstack-nvidia-0.6.0)") + logger.error("Expected format: dstack- (or a legacy dstack--)") return version = version_match.group(1) - download_url = f"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/Dstack-TEE/meta-dstack/releases/download/v{version}/{os_image}-uki.tar.gz" + download_url = guest_os_release_url(os_image, version) download_tar = target_dir / f"{os_image}-uki.tar.gz" if download_tar.exists(): @@ -1044,10 +1068,11 @@ class CloudDeploymentManager: try: # Use curl to download with progress bar - subprocess.run( - ["curl", "-L", "-o", str(download_tar), download_url], - check=True + result = subprocess.run( + ["curl", "-fL", "-o", str(download_tar), download_url], + check=False ) + result.check_returncode() logger.info(f"Successfully downloaded to {download_tar}") except subprocess.CalledProcessError as e: logger.error(f"Failed to download image: {e}") diff --git a/scripts/config-fw.sh b/dstack/scripts/config-fw.sh similarity index 100% rename from scripts/config-fw.sh rename to dstack/scripts/config-fw.sh diff --git a/scripts/dhcp-notify.sh b/dstack/scripts/dhcp-notify.sh similarity index 100% rename from scripts/dhcp-notify.sh rename to dstack/scripts/dhcp-notify.sh diff --git a/scripts/install.sh b/dstack/scripts/install.sh similarity index 90% rename from scripts/install.sh rename to dstack/scripts/install.sh index 0a1afe882..af245443a 100755 --- a/scripts/install.sh +++ b/dstack/scripts/install.sh @@ -14,8 +14,8 @@ usage() { Install dstackup from source. Usage: - scripts/install.sh [options] - curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/scripts/install.sh | sh + dstack/scripts/install.sh [options] + curl -fsSL https://raw.githubusercontent.com/Dstack-TEE/dstack/master/dstack/scripts/install.sh | sh Options: --repo URL Git repository to clone when not run from a checkout. @@ -115,7 +115,7 @@ need_cmd() { fi } -is_checkout() { +is_core_checkout() { [ -f "$1/Cargo.toml" ] && [ -d "$1/crates/dstackup" ] && [ -d "$1/crates/dstack-cli" ] && @@ -123,6 +123,18 @@ is_checkout() { [ -d "$1/supervisor" ] } +is_checkout() { + is_core_checkout "$1/dstack" || is_core_checkout "$1" +} + +core_dir() { + if is_core_checkout "$1/dstack"; then + echo "$1/dstack" + else + echo "$1" + fi +} + abs_dir() { (cd "$1" && pwd) } @@ -131,7 +143,10 @@ script_checkout() { case "$0" in */*) script_dir=$(dirname "$0") - if [ -d "$script_dir/.." ] && is_checkout "$script_dir/.."; then + if [ -d "$script_dir/../.." ] && is_checkout "$script_dir/../.."; then + abs_dir "$script_dir/../.." + return 0 + elif [ -d "$script_dir/.." ] && is_checkout "$script_dir/.."; then abs_dir "$script_dir/.." return 0 fi @@ -154,7 +169,7 @@ resolve_source() { need_cmd git if [ -n "$src" ] && [ -e "$src" ]; then - if ! is_checkout "$src" || [ ! -d "$src/.git" ]; then + if ! is_checkout "$src" || ! git -C "$src" rev-parse --git-dir >/dev/null 2>&1; then echo "error: $src exists but is not a dstack git checkout" >&2 exit 1 fi @@ -224,6 +239,7 @@ need_cmd cargo need_cmd install checkout=$(resolve_source) +core_checkout=$(core_dir "$checkout") bin_dir="$prefix/bin" if [ "$no_sudo" -eq 0 ] && [ "$(id -u)" -ne 0 ]; then @@ -241,13 +257,13 @@ fi echo "building dstackup from $checkout" ( - cd "$checkout" + cd "$core_checkout" cargo build --release \ -p dstackup ) install_bin() { - src_bin="$checkout/target/release/$1" + src_bin="$core_checkout/target/release/$1" dest_bin="$bin_dir/$2" if [ ! -f "$src_bin" ]; then echo "error: expected binary not found: $src_bin" >&2 diff --git a/scripts/setup-bridge.sh b/dstack/scripts/setup-bridge.sh similarity index 100% rename from scripts/setup-bridge.sh rename to dstack/scripts/setup-bridge.sh diff --git a/serde-duration/Cargo.toml b/dstack/serde-duration/Cargo.toml similarity index 100% rename from serde-duration/Cargo.toml rename to dstack/serde-duration/Cargo.toml diff --git a/serde-duration/src/lib.rs b/dstack/serde-duration/src/lib.rs similarity index 100% rename from serde-duration/src/lib.rs rename to dstack/serde-duration/src/lib.rs diff --git a/sev-snp-attest/Cargo.toml b/dstack/sev-snp-attest/Cargo.toml similarity index 100% rename from sev-snp-attest/Cargo.toml rename to dstack/sev-snp-attest/Cargo.toml diff --git a/sev-snp-attest/src/lib.rs b/dstack/sev-snp-attest/src/lib.rs similarity index 100% rename from sev-snp-attest/src/lib.rs rename to dstack/sev-snp-attest/src/lib.rs diff --git a/sev-snp-qvl/Cargo.toml b/dstack/sev-snp-qvl/Cargo.toml similarity index 100% rename from sev-snp-qvl/Cargo.toml rename to dstack/sev-snp-qvl/Cargo.toml diff --git a/sev-snp-qvl/src/lib.rs b/dstack/sev-snp-qvl/src/lib.rs similarity index 100% rename from sev-snp-qvl/src/lib.rs rename to dstack/sev-snp-qvl/src/lib.rs diff --git a/size-parser/Cargo.toml b/dstack/size-parser/Cargo.toml similarity index 100% rename from size-parser/Cargo.toml rename to dstack/size-parser/Cargo.toml diff --git a/size-parser/README.md b/dstack/size-parser/README.md similarity index 100% rename from size-parser/README.md rename to dstack/size-parser/README.md diff --git a/size-parser/src/lib.rs b/dstack/size-parser/src/lib.rs similarity index 100% rename from size-parser/src/lib.rs rename to dstack/size-parser/src/lib.rs diff --git a/sodiumbox/Cargo.toml b/dstack/sodiumbox/Cargo.toml similarity index 100% rename from sodiumbox/Cargo.toml rename to dstack/sodiumbox/Cargo.toml diff --git a/sodiumbox/README.md b/dstack/sodiumbox/README.md similarity index 87% rename from sodiumbox/README.md rename to dstack/sodiumbox/README.md index 9694a7f7e..c3de9904e 100644 --- a/sodiumbox/README.md +++ b/dstack/sodiumbox/README.md @@ -47,7 +47,7 @@ match result { This crate is licensed under either of: -- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +- Apache License, Version 2.0, ([LICENSE-APACHE](../../LICENSES/Apache-2.0.txt) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](../../LICENSES/MIT.txt) or http://opensource.org/licenses/MIT) at your option. diff --git a/sodiumbox/src/lib.rs b/dstack/sodiumbox/src/lib.rs similarity index 99% rename from sodiumbox/src/lib.rs rename to dstack/sodiumbox/src/lib.rs index 8375776c6..26f6ac827 100644 --- a/sodiumbox/src/lib.rs +++ b/dstack/sodiumbox/src/lib.rs @@ -59,9 +59,7 @@ fn derive_nonce(ephemeral_pk: &[u8], recipient_pk: &[u8]) -> Result [u8; 32] { let hsalsa_nonce = [0u8; 16]; let key = salsa20::hsalsa::(shared_secret.into(), &hsalsa_nonce.into()); - let mut result = [0u8; 32]; - result.copy_from_slice(key.as_slice()); - result + key.into() } /// Creates a sealed box for a message using a recipient's public key. diff --git a/supervisor/Cargo.toml b/dstack/supervisor/Cargo.toml similarity index 100% rename from supervisor/Cargo.toml rename to dstack/supervisor/Cargo.toml diff --git a/supervisor/client/Cargo.toml b/dstack/supervisor/client/Cargo.toml similarity index 100% rename from supervisor/client/Cargo.toml rename to dstack/supervisor/client/Cargo.toml diff --git a/supervisor/client/src/lib.rs b/dstack/supervisor/client/src/lib.rs similarity index 100% rename from supervisor/client/src/lib.rs rename to dstack/supervisor/client/src/lib.rs diff --git a/supervisor/client/src/main.rs b/dstack/supervisor/client/src/main.rs similarity index 100% rename from supervisor/client/src/main.rs rename to dstack/supervisor/client/src/main.rs diff --git a/supervisor/src/lib.rs b/dstack/supervisor/src/lib.rs similarity index 100% rename from supervisor/src/lib.rs rename to dstack/supervisor/src/lib.rs diff --git a/supervisor/src/main.rs b/dstack/supervisor/src/main.rs similarity index 100% rename from supervisor/src/main.rs rename to dstack/supervisor/src/main.rs diff --git a/supervisor/src/process.rs b/dstack/supervisor/src/process.rs similarity index 100% rename from supervisor/src/process.rs rename to dstack/supervisor/src/process.rs diff --git a/supervisor/src/supervisor.rs b/dstack/supervisor/src/supervisor.rs similarity index 100% rename from supervisor/src/supervisor.rs rename to dstack/supervisor/src/supervisor.rs diff --git a/supervisor/src/web_api.rs b/dstack/supervisor/src/web_api.rs similarity index 100% rename from supervisor/src/web_api.rs rename to dstack/supervisor/src/web_api.rs diff --git a/supervisor/supervisor.toml b/dstack/supervisor/supervisor.toml similarity index 100% rename from supervisor/supervisor.toml rename to dstack/supervisor/supervisor.toml diff --git a/supervisor/tests/test-cli.sh b/dstack/supervisor/tests/test-cli.sh similarity index 99% rename from supervisor/tests/test-cli.sh rename to dstack/supervisor/tests/test-cli.sh index 2985396aa..6b93a6a06 100755 --- a/supervisor/tests/test-cli.sh +++ b/dstack/supervisor/tests/test-cli.sh @@ -130,4 +130,4 @@ ${CLI} shutdown 2>/dev/null kill $(cat $PIDFILE) 2>/dev/null rm -f $UDS $PIDFILE -info "CLI testing completed!" +info "CLI testing completed!" diff --git a/supervisor/tests/test.sh b/dstack/supervisor/tests/test.sh similarity index 98% rename from supervisor/tests/test.sh rename to dstack/supervisor/tests/test.sh index b1ec27e47..7205a81db 100755 --- a/supervisor/tests/test.sh +++ b/dstack/supervisor/tests/test.sh @@ -132,7 +132,7 @@ RES=$(http POST "${BASE_URL}/deploy" ' }, "cwd": "/var/tmp", "stdout": "/tmp/stdout.log", - "stderr": "/tmp/stderr.log", + "stderr": "/tmp/stderr.log", "pidfile": "/tmp/test.pid" }') assert_eq "$RES" '{"data":null}' "Deploy should return empty object" @@ -153,10 +153,10 @@ sleep 1 # Check stdout file exists and contains expected output info "Testing stdout file contents" -assert_eq "$(cat "/tmp/stdout.log")" "hello world, FOO=bar, pwd=/var/tmp" +assert_eq "$(cat "/tmp/stdout.log")" "hello world, FOO=bar, pwd=/var/tmp" # Check stderr file exists and contains expected output -info "Testing stderr file contents" +info "Testing stderr file contents" assert_eq "$(cat "/tmp/stderr.log")" "hello stderr" "Stderr file should contain expected output" # Check pid file exists and contains expected pid diff --git a/tdx-attest/Cargo.toml b/dstack/tdx-attest/Cargo.toml similarity index 100% rename from tdx-attest/Cargo.toml rename to dstack/tdx-attest/Cargo.toml diff --git a/tdx-attest/examples/test_tdx.rs b/dstack/tdx-attest/examples/test_tdx.rs similarity index 100% rename from tdx-attest/examples/test_tdx.rs rename to dstack/tdx-attest/examples/test_tdx.rs diff --git a/tdx-attest/src/dummy.rs b/dstack/tdx-attest/src/dummy.rs similarity index 100% rename from tdx-attest/src/dummy.rs rename to dstack/tdx-attest/src/dummy.rs diff --git a/tdx-attest/src/lib.rs b/dstack/tdx-attest/src/lib.rs similarity index 100% rename from tdx-attest/src/lib.rs rename to dstack/tdx-attest/src/lib.rs diff --git a/tdx-attest/src/linux.rs b/dstack/tdx-attest/src/linux.rs similarity index 100% rename from tdx-attest/src/linux.rs rename to dstack/tdx-attest/src/linux.rs diff --git a/tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel-2.snap b/dstack/tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel-2.snap similarity index 100% rename from tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel-2.snap rename to dstack/tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel-2.snap diff --git a/tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel.snap b/dstack/tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel.snap similarity index 100% rename from tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel.snap rename to dstack/tdx-attest/src/snapshots/tdx_attest__eventlog__tests__parse_ccel.snap diff --git a/test-scripts/get-app-key.sh b/dstack/test-scripts/get-app-key.sh similarity index 100% rename from test-scripts/get-app-key.sh rename to dstack/test-scripts/get-app-key.sh diff --git a/test-scripts/inspect-cert.sh b/dstack/test-scripts/inspect-cert.sh similarity index 78% rename from test-scripts/inspect-cert.sh rename to dstack/test-scripts/inspect-cert.sh index 141ef4e8d..6ecb93389 100755 --- a/test-scripts/inspect-cert.sh +++ b/dstack/test-scripts/inspect-cert.sh @@ -3,4 +3,4 @@ # SPDX-FileCopyrightText: © 2025 Phala Network # SPDX-License-Identifier: Apache-2.0 -openssl x509 -text -noout -in $1 \ No newline at end of file +openssl x509 -text -noout -in $1 diff --git a/test-scripts/snp-e2e-smoke.sh b/dstack/test-scripts/snp-e2e-smoke.sh similarity index 97% rename from test-scripts/snp-e2e-smoke.sh rename to dstack/test-scripts/snp-e2e-smoke.sh index 30ec9eb7f..ea7d1373e 100755 --- a/test-scripts/snp-e2e-smoke.sh +++ b/dstack/test-scripts/snp-e2e-smoke.sh @@ -10,14 +10,14 @@ # built release binaries. # # Minimal setup used by the original smoke: -# cargo build --release -p dstack-vmm -p supervisor -p dstack-kms -# export DSTACK_SNP_SMOKE_BIN_DIR=$PWD/target/release +# cargo build --manifest-path dstack/Cargo.toml --release -p dstack-vmm -p supervisor -p dstack-kms +# export DSTACK_SNP_SMOKE_BIN_DIR=$PWD/dstack/target/release # export DSTACK_SNP_SMOKE_ALLOW_OUT_OF_DATE_TCB=1 # lab hosts only; auth API policy # test-scripts/snp-e2e-smoke.sh # # Useful overrides: # DSTACK_SNP_SMOKE_BASE=$HOME/dstack-snp-e2e -# DSTACK_SNP_SMOKE_REPO=$PWD +# DSTACK_SNP_SMOKE_REPO=$PWD/dstack # DSTACK_SNP_SMOKE_QEMU=/opt/AMDSEV/usr/local/bin/qemu-system-x86_64 # DSTACK_SNP_SMOKE_OVMF=/opt/AMDSEV/usr/local/share/qemu/OVMF.fd # DSTACK_SNP_SMOKE_IMAGE_URL=https://github.com/Dstack-TEE/meta-dstack/releases/download/v0.5.11/dstack-dev-0.5.11.tar.gz @@ -46,16 +46,18 @@ # https://cors.litgateway.com/https://kdsintf.amd.com/vcek/v1 # This is an external collateral-fetch boundary, not a guest boot or KMS startup # failure. -# One reproducible way is to build meta-dstack with its dstack submodule checked -# out to this PR branch, set the Yocto build MACHINE to `sev-snp` (not the +# One reproducible way is to build os/yocto from the same monorepo revision, +# set the Yocto build MACHINE to `sev-snp` (not the # default `tdx`, otherwise the guest kernel can miss AMD memory-encryption # support and reset immediately after OVMF loads the kernel/initrd), then point # DSTACK_SNP_SMOKE_IMAGE_NAME at the resulting dstack-dev image directory. set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CORE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" BASE="${DSTACK_SNP_SMOKE_BASE:-$HOME/dstack-snp-e2e}" -REPO="${DSTACK_SNP_SMOKE_REPO:-$(pwd)}" +REPO="${DSTACK_SNP_SMOKE_REPO:-$CORE_DIR}" BIN="${DSTACK_SNP_SMOKE_BIN_DIR:-$REPO/target/release}" ART="$BASE/artifacts" LOG="$ART/snp-e2e-smoke.log" diff --git a/tests/docs/kms-bootstrap-onboard.md b/dstack/tests/docs/kms-bootstrap-onboard.md similarity index 100% rename from tests/docs/kms-bootstrap-onboard.md rename to dstack/tests/docs/kms-bootstrap-onboard.md diff --git a/tests/docs/kms-self-authorization.md b/dstack/tests/docs/kms-self-authorization.md similarity index 99% rename from tests/docs/kms-self-authorization.md rename to dstack/tests/docs/kms-self-authorization.md index 7df4ad18f..fbde050e9 100644 --- a/tests/docs/kms-self-authorization.md +++ b/dstack/tests/docs/kms-self-authorization.md @@ -162,7 +162,7 @@ and make sure you still have a way to update allow/deny policy during the test. ### 5.3 If using host-local `auth-simple`, install and start two instances ```bash -cd "$REPO_ROOT/kms/auth-simple" +cd "$REPO_ROOT/dstack/kms/auth-simple" bun install ``` @@ -201,7 +201,7 @@ These placeholder configs intentionally deny all KMS boots until you populate `k Start the services: ```bash -cd "$REPO_ROOT/kms/auth-simple" +cd "$REPO_ROOT/dstack/kms/auth-simple" AUTH_CONFIG_PATH=/tmp/kms-self-auth/auth-src.json PORT=3101 bun run start \ >/tmp/kms-self-auth/auth-src.log 2>&1 & echo $! >/tmp/kms-self-auth/auth-src.pid diff --git a/tpm-attest/Cargo.toml b/dstack/tpm-attest/Cargo.toml similarity index 100% rename from tpm-attest/Cargo.toml rename to dstack/tpm-attest/Cargo.toml diff --git a/tpm-attest/src/esapi.rs b/dstack/tpm-attest/src/esapi.rs similarity index 100% rename from tpm-attest/src/esapi.rs rename to dstack/tpm-attest/src/esapi.rs diff --git a/tpm-attest/src/gcp_ak.rs b/dstack/tpm-attest/src/gcp_ak.rs similarity index 100% rename from tpm-attest/src/gcp_ak.rs rename to dstack/tpm-attest/src/gcp_ak.rs diff --git a/tpm-attest/src/lib.rs b/dstack/tpm-attest/src/lib.rs similarity index 100% rename from tpm-attest/src/lib.rs rename to dstack/tpm-attest/src/lib.rs diff --git a/tpm-attest/tests/tpm_quote_sample.bin b/dstack/tpm-attest/tests/tpm_quote_sample.bin similarity index 100% rename from tpm-attest/tests/tpm_quote_sample.bin rename to dstack/tpm-attest/tests/tpm_quote_sample.bin diff --git a/tpm-qvl/Cargo.toml b/dstack/tpm-qvl/Cargo.toml similarity index 100% rename from tpm-qvl/Cargo.toml rename to dstack/tpm-qvl/Cargo.toml diff --git a/tpm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem b/dstack/tpm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem similarity index 100% rename from tpm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem rename to dstack/tpm-qvl/certs/AWS_NitroEnclaves_Root-G1.pem diff --git a/tpm-qvl/certs/gcp-root-ca.pem b/dstack/tpm-qvl/certs/gcp-root-ca.pem similarity index 100% rename from tpm-qvl/certs/gcp-root-ca.pem rename to dstack/tpm-qvl/certs/gcp-root-ca.pem diff --git a/tpm-qvl/src/collateral.rs b/dstack/tpm-qvl/src/collateral.rs similarity index 100% rename from tpm-qvl/src/collateral.rs rename to dstack/tpm-qvl/src/collateral.rs diff --git a/tpm-qvl/src/lib.rs b/dstack/tpm-qvl/src/lib.rs similarity index 100% rename from tpm-qvl/src/lib.rs rename to dstack/tpm-qvl/src/lib.rs diff --git a/tpm-qvl/src/verify.rs b/dstack/tpm-qvl/src/verify.rs similarity index 100% rename from tpm-qvl/src/verify.rs rename to dstack/tpm-qvl/src/verify.rs diff --git a/tpm-types/Cargo.toml b/dstack/tpm-types/Cargo.toml similarity index 100% rename from tpm-types/Cargo.toml rename to dstack/tpm-types/Cargo.toml diff --git a/tpm-types/src/lib.rs b/dstack/tpm-types/src/lib.rs similarity index 100% rename from tpm-types/src/lib.rs rename to dstack/tpm-types/src/lib.rs diff --git a/tpm2/Cargo.toml b/dstack/tpm2/Cargo.toml similarity index 100% rename from tpm2/Cargo.toml rename to dstack/tpm2/Cargo.toml diff --git a/tpm2/src/bin/tpm2-test.rs b/dstack/tpm2/src/bin/tpm2-test.rs similarity index 100% rename from tpm2/src/bin/tpm2-test.rs rename to dstack/tpm2/src/bin/tpm2-test.rs diff --git a/tpm2/src/commands.rs b/dstack/tpm2/src/commands.rs similarity index 100% rename from tpm2/src/commands.rs rename to dstack/tpm2/src/commands.rs diff --git a/tpm2/src/constants.rs b/dstack/tpm2/src/constants.rs similarity index 100% rename from tpm2/src/constants.rs rename to dstack/tpm2/src/constants.rs diff --git a/tpm2/src/device.rs b/dstack/tpm2/src/device.rs similarity index 100% rename from tpm2/src/device.rs rename to dstack/tpm2/src/device.rs diff --git a/tpm2/src/lib.rs b/dstack/tpm2/src/lib.rs similarity index 100% rename from tpm2/src/lib.rs rename to dstack/tpm2/src/lib.rs diff --git a/tpm2/src/marshal.rs b/dstack/tpm2/src/marshal.rs similarity index 100% rename from tpm2/src/marshal.rs rename to dstack/tpm2/src/marshal.rs diff --git a/tpm2/src/session.rs b/dstack/tpm2/src/session.rs similarity index 100% rename from tpm2/src/session.rs rename to dstack/tpm2/src/session.rs diff --git a/tpm2/src/types.rs b/dstack/tpm2/src/types.rs similarity index 100% rename from tpm2/src/types.rs rename to dstack/tpm2/src/types.rs diff --git a/verifier/Cargo.toml b/dstack/verifier/Cargo.toml similarity index 100% rename from verifier/Cargo.toml rename to dstack/verifier/Cargo.toml diff --git a/verifier/README.md b/dstack/verifier/README.md similarity index 100% rename from verifier/README.md rename to dstack/verifier/README.md diff --git a/verifier/builder/Dockerfile b/dstack/verifier/builder/Dockerfile similarity index 91% rename from verifier/builder/Dockerfile rename to dstack/verifier/builder/Dockerfile index 06070f9db..f8ad5274a 100644 --- a/verifier/builder/Dockerfile +++ b/dstack/verifier/builder/Dockerfile @@ -23,11 +23,11 @@ RUN apt-get update && \ ca-certificates \ curl && \ rm -rf /var/lib/apt/lists/* /var/log/* /var/cache/ldconfig/aux-cache -RUN git clone ${DSTACK_SRC_URL} dstack && \ - cd dstack && \ +RUN git clone ${DSTACK_SRC_URL} repo && \ + cd repo && \ git checkout ${DSTACK_REV} RUN rustup target add x86_64-unknown-linux-musl -RUN cd dstack && cargo build --release -p dstack-verifier --target x86_64-unknown-linux-musl +RUN cd repo/dstack && cargo build --release -p dstack-verifier --target x86_64-unknown-linux-musl RUN echo "${DSTACK_REV}" > /build/.GIT_REV FROM debian:bookworm@sha256:0d8498a0e9e6a60011df39aab78534cfe940785e7c59d19dfae1eb53ea59babe AS acpi-builder @@ -77,7 +77,7 @@ RUN ./pin-packages.sh ./pinned-packages.txt && \ libglib2.0-0 \ libslirp0 \ && rm -rf /var/lib/apt/lists/* /var/log/* /var/cache/ldconfig/aux-cache -COPY --from=verifier-builder /build/dstack/target/x86_64-unknown-linux-musl/release/dstack-verifier /usr/local/bin/dstack-verifier +COPY --from=verifier-builder /build/repo/dstack/target/x86_64-unknown-linux-musl/release/dstack-verifier /usr/local/bin/dstack-verifier COPY --from=verifier-builder /build/.GIT_REV /etc/ COPY --from=acpi-builder /usr/local/bin/dstack-acpi-tables /usr/local/bin/dstack-acpi-tables COPY --from=acpi-builder /usr/local/share/qemu /usr/local/share/qemu diff --git a/verifier/builder/build-image.sh b/dstack/verifier/builder/build-image.sh similarity index 95% rename from verifier/builder/build-image.sh rename to dstack/verifier/builder/build-image.sh index 002a26e17..df3bfddbd 100755 --- a/verifier/builder/build-image.sh +++ b/dstack/verifier/builder/build-image.sh @@ -12,7 +12,7 @@ CONTEXT_DIR=$(dirname "$SCRIPT_DIR") SHARED_DIR="$SCRIPT_DIR/shared" DOCKERFILE="$SCRIPT_DIR/Dockerfile" -source "$REPO_ROOT/build/shared/build-lib.sh" +source "$REPO_ROOT/dstack/build/shared/build-lib.sh" NAME=${1:-} if [ -z "$NAME" ]; then diff --git a/verifier/builder/shared/builder-pinned-packages.txt b/dstack/verifier/builder/shared/builder-pinned-packages.txt similarity index 100% rename from verifier/builder/shared/builder-pinned-packages.txt rename to dstack/verifier/builder/shared/builder-pinned-packages.txt diff --git a/verifier/builder/shared/pinned-packages.txt b/dstack/verifier/builder/shared/pinned-packages.txt similarity index 100% rename from verifier/builder/shared/pinned-packages.txt rename to dstack/verifier/builder/shared/pinned-packages.txt diff --git a/verifier/builder/shared/qemu-pinned-packages.txt b/dstack/verifier/builder/shared/qemu-pinned-packages.txt similarity index 100% rename from verifier/builder/shared/qemu-pinned-packages.txt rename to dstack/verifier/builder/shared/qemu-pinned-packages.txt diff --git a/verifier/dstack-verifier.toml b/dstack/verifier/dstack-verifier.toml similarity index 92% rename from verifier/dstack-verifier.toml rename to dstack/verifier/dstack-verifier.toml index 8c8a9b89e..b098c533a 100644 --- a/verifier/dstack-verifier.toml +++ b/dstack/verifier/dstack-verifier.toml @@ -16,4 +16,4 @@ image_download_url = "https://download.dstack.org/os-images/mr_{OS_IMAGE_HASH}.t image_download_timeout_secs = 300 # Optional PCCS URL for quote verification -# pccs_url = "https://pccs.phala.network" \ No newline at end of file +# pccs_url = "https://pccs.phala.network" diff --git a/verifier/fixtures/quote-report.json b/dstack/verifier/fixtures/quote-report.json similarity index 100% rename from verifier/fixtures/quote-report.json rename to dstack/verifier/fixtures/quote-report.json diff --git a/verifier/fixtures/sev-snp-attestation.json b/dstack/verifier/fixtures/sev-snp-attestation.json similarity index 100% rename from verifier/fixtures/sev-snp-attestation.json rename to dstack/verifier/fixtures/sev-snp-attestation.json diff --git a/verifier/fixtures/sev-snp.README.md b/dstack/verifier/fixtures/sev-snp.README.md similarity index 100% rename from verifier/fixtures/sev-snp.README.md rename to dstack/verifier/fixtures/sev-snp.README.md diff --git a/verifier/fixtures/tdx-lite-attestation.json b/dstack/verifier/fixtures/tdx-lite-attestation.json similarity index 100% rename from verifier/fixtures/tdx-lite-attestation.json rename to dstack/verifier/fixtures/tdx-lite-attestation.json diff --git a/verifier/fixtures/tdx-lite-getquote.json b/dstack/verifier/fixtures/tdx-lite-getquote.json similarity index 100% rename from verifier/fixtures/tdx-lite-getquote.json rename to dstack/verifier/fixtures/tdx-lite-getquote.json diff --git a/verifier/fixtures/tdx-lite.README.md b/dstack/verifier/fixtures/tdx-lite.README.md similarity index 100% rename from verifier/fixtures/tdx-lite.README.md rename to dstack/verifier/fixtures/tdx-lite.README.md diff --git a/verifier/src/lib.rs b/dstack/verifier/src/lib.rs similarity index 100% rename from verifier/src/lib.rs rename to dstack/verifier/src/lib.rs diff --git a/verifier/src/main.rs b/dstack/verifier/src/main.rs similarity index 100% rename from verifier/src/main.rs rename to dstack/verifier/src/main.rs diff --git a/verifier/src/types.rs b/dstack/verifier/src/types.rs similarity index 100% rename from verifier/src/types.rs rename to dstack/verifier/src/types.rs diff --git a/verifier/src/verification.rs b/dstack/verifier/src/verification.rs similarity index 100% rename from verifier/src/verification.rs rename to dstack/verifier/src/verification.rs diff --git a/verifier/test.sh b/dstack/verifier/test.sh similarity index 100% rename from verifier/test.sh rename to dstack/verifier/test.sh diff --git a/vmm/Cargo.toml b/dstack/vmm/Cargo.toml similarity index 100% rename from vmm/Cargo.toml rename to dstack/vmm/Cargo.toml diff --git a/vmm/build.rs b/dstack/vmm/build.rs similarity index 100% rename from vmm/build.rs rename to dstack/vmm/build.rs diff --git a/vmm/requirements.txt b/dstack/vmm/requirements.txt similarity index 100% rename from vmm/requirements.txt rename to dstack/vmm/requirements.txt diff --git a/vmm/rpc/Cargo.toml b/dstack/vmm/rpc/Cargo.toml similarity index 100% rename from vmm/rpc/Cargo.toml rename to dstack/vmm/rpc/Cargo.toml diff --git a/vmm/rpc/build.rs b/dstack/vmm/rpc/build.rs similarity index 100% rename from vmm/rpc/build.rs rename to dstack/vmm/rpc/build.rs diff --git a/vmm/rpc/proto/prpc.proto b/dstack/vmm/rpc/proto/prpc.proto similarity index 99% rename from vmm/rpc/proto/prpc.proto rename to dstack/vmm/rpc/proto/prpc.proto index cd5a5d471..ae9e61f9f 100644 --- a/vmm/rpc/proto/prpc.proto +++ b/dstack/vmm/rpc/proto/prpc.proto @@ -10,4 +10,3 @@ package prpc; message PrpcError { string message = 1; } - diff --git a/vmm/rpc/proto/vmm_rpc.proto b/dstack/vmm/rpc/proto/vmm_rpc.proto similarity index 100% rename from vmm/rpc/proto/vmm_rpc.proto rename to dstack/vmm/rpc/proto/vmm_rpc.proto diff --git a/vmm/rpc/src/generated.rs b/dstack/vmm/rpc/src/generated.rs similarity index 100% rename from vmm/rpc/src/generated.rs rename to dstack/vmm/rpc/src/generated.rs diff --git a/vmm/rpc/src/lib.rs b/dstack/vmm/rpc/src/lib.rs similarity index 100% rename from vmm/rpc/src/lib.rs rename to dstack/vmm/rpc/src/lib.rs diff --git a/vmm/src/app.rs b/dstack/vmm/src/app.rs similarity index 100% rename from vmm/src/app.rs rename to dstack/vmm/src/app.rs diff --git a/vmm/src/app/id_pool.rs b/dstack/vmm/src/app/id_pool.rs similarity index 100% rename from vmm/src/app/id_pool.rs rename to dstack/vmm/src/app/id_pool.rs diff --git a/vmm/src/app/image.rs b/dstack/vmm/src/app/image.rs similarity index 100% rename from vmm/src/app/image.rs rename to dstack/vmm/src/app/image.rs diff --git a/vmm/src/app/qemu.rs b/dstack/vmm/src/app/qemu.rs similarity index 100% rename from vmm/src/app/qemu.rs rename to dstack/vmm/src/app/qemu.rs diff --git a/vmm/src/app/registry.rs b/dstack/vmm/src/app/registry.rs similarity index 100% rename from vmm/src/app/registry.rs rename to dstack/vmm/src/app/registry.rs diff --git a/vmm/src/config.rs b/dstack/vmm/src/config.rs similarity index 100% rename from vmm/src/config.rs rename to dstack/vmm/src/config.rs diff --git a/vmm/src/console_v0.html b/dstack/vmm/src/console_v0.html similarity index 100% rename from vmm/src/console_v0.html rename to dstack/vmm/src/console_v0.html diff --git a/vmm/src/discovery.rs b/dstack/vmm/src/discovery.rs similarity index 100% rename from vmm/src/discovery.rs rename to dstack/vmm/src/discovery.rs diff --git a/vmm/src/guest_api_service.rs b/dstack/vmm/src/guest_api_service.rs similarity index 100% rename from vmm/src/guest_api_service.rs rename to dstack/vmm/src/guest_api_service.rs diff --git a/vmm/src/host_api_service.rs b/dstack/vmm/src/host_api_service.rs similarity index 100% rename from vmm/src/host_api_service.rs rename to dstack/vmm/src/host_api_service.rs diff --git a/vmm/src/main.rs b/dstack/vmm/src/main.rs similarity index 100% rename from vmm/src/main.rs rename to dstack/vmm/src/main.rs diff --git a/vmm/src/main_routes.rs b/dstack/vmm/src/main_routes.rs similarity index 100% rename from vmm/src/main_routes.rs rename to dstack/vmm/src/main_routes.rs diff --git a/vmm/src/main_service.rs b/dstack/vmm/src/main_service.rs similarity index 100% rename from vmm/src/main_service.rs rename to dstack/vmm/src/main_service.rs diff --git a/vmm/src/one_shot.rs b/dstack/vmm/src/one_shot.rs similarity index 100% rename from vmm/src/one_shot.rs rename to dstack/vmm/src/one_shot.rs diff --git a/vmm/src/openapi.rs b/dstack/vmm/src/openapi.rs similarity index 100% rename from vmm/src/openapi.rs rename to dstack/vmm/src/openapi.rs diff --git a/vmm/src/setup-user.sh b/dstack/vmm/src/setup-user.sh similarity index 100% rename from vmm/src/setup-user.sh rename to dstack/vmm/src/setup-user.sh diff --git a/vmm/src/tests/test-compose.sh b/dstack/vmm/src/tests/test-compose.sh similarity index 98% rename from vmm/src/tests/test-compose.sh rename to dstack/vmm/src/tests/test-compose.sh index a68a8fbcd..a87a2df98 100755 --- a/vmm/src/tests/test-compose.sh +++ b/dstack/vmm/src/tests/test-compose.sh @@ -4,6 +4,8 @@ # # SPDX-License-Identifier: Apache-2.0 +# shellcheck disable=SC2317 # cleanup is invoked through an EXIT trap + # Test script for vmm-cli.py compose subcommand # Tests the refactored create_app_compose method that accepts args directly @@ -17,7 +19,8 @@ NC='\033[0m' # No Color # Test directory TEST_DIR="/tmp/vmm-cli-compose-test" -VMM_CLI="/home/kvin/sdc/home/meta-dstack/dstack/vmm/src/vmm-cli.py" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VMM_CLI="$SCRIPT_DIR/../vmm-cli.py" # Test counter TESTS_PASSED=0 @@ -45,7 +48,7 @@ cleanup() { setup() { echo -e "${YELLOW}Setting up test environment...${NC}" mkdir -p "$TEST_DIR" - + # Create test docker-compose.yml cat > "$TEST_DIR/docker-compose.yml" << 'EOF' version: '3' @@ -83,12 +86,12 @@ EOF # Test functions test_basic_compose() { print_test "Basic compose functionality with minimal parameters" - + if python3 "$VMM_CLI" compose \ --name test-basic \ --docker-compose "$TEST_DIR/docker-compose.yml" \ --output "$TEST_DIR/basic-output.json" > /dev/null 2>&1; then - + if [[ -f "$TEST_DIR/basic-output.json" ]]; then # Verify JSON structure if jq -e '.name == "test-basic"' "$TEST_DIR/basic-output.json" > /dev/null && \ @@ -113,7 +116,7 @@ test_basic_compose() { test_full_compose() { print_test "Full compose functionality with all optional parameters" - + if python3 "$VMM_CLI" compose \ --name test-full \ --docker-compose "$TEST_DIR/docker-compose.yml" \ @@ -127,7 +130,7 @@ test_full_compose() { --public-sysinfo \ --no-instance-id \ --output "$TEST_DIR/full-output.json" > /dev/null 2>&1; then - + if [[ -f "$TEST_DIR/full-output.json" ]]; then # Verify all options are set correctly if jq -e '.name == "test-full"' "$TEST_DIR/full-output.json" > /dev/null && \ @@ -157,13 +160,13 @@ test_full_compose() { test_env_parsing() { print_test "Environment variable parsing" - + python3 "$VMM_CLI" compose \ --name test-env \ --docker-compose "$TEST_DIR/docker-compose.yml" \ --env-file "$TEST_DIR/test.env" \ --output "$TEST_DIR/env-output.json" > /dev/null 2>&1 - + # Check if all environment variables are in allowed_envs if jq -e '.allowed_envs | contains(["API_KEY", "DEBUG", "PORT", "DATABASE_URL"])' "$TEST_DIR/env-output.json" > /dev/null; then print_success "Environment parsing test passed - all env vars included" @@ -175,12 +178,12 @@ test_env_parsing() { test_docker_compose_embedding() { print_test "Docker compose file embedding" - + python3 "$VMM_CLI" compose \ --name test-docker \ --docker-compose "$TEST_DIR/docker-compose.yml" \ --output "$TEST_DIR/docker-output.json" > /dev/null 2>&1 - + # Check if docker-compose content is properly embedded if jq -e '.docker_compose_file | contains("nginx:latest")' "$TEST_DIR/docker-output.json" > /dev/null && \ jq -e '.docker_compose_file | contains("redis:alpine")' "$TEST_DIR/docker-output.json" > /dev/null; then @@ -193,13 +196,13 @@ test_docker_compose_embedding() { test_prelaunch_script() { print_test "Prelaunch script embedding" - + python3 "$VMM_CLI" compose \ --name test-prelaunch \ --docker-compose "$TEST_DIR/docker-compose.yml" \ --prelaunch-script "$TEST_DIR/prelaunch.sh" \ --output "$TEST_DIR/prelaunch-output.json" > /dev/null 2>&1 - + # Check if prelaunch script is properly embedded if jq -e '.pre_launch_script | contains("Starting application...")' "$TEST_DIR/prelaunch-output.json" > /dev/null && \ jq -e '.pre_launch_script | contains("#!/bin/bash")' "$TEST_DIR/prelaunch-output.json" > /dev/null; then @@ -212,7 +215,7 @@ test_prelaunch_script() { test_error_handling() { print_test "Error handling for missing files" - + # Test missing docker-compose file if python3 "$VMM_CLI" compose \ --name test-error \ @@ -227,7 +230,7 @@ test_error_handling() { test_help_command() { print_test "Help command functionality" - + if python3 "$VMM_CLI" compose --help > /dev/null 2>&1; then print_success "Help command test passed - help displayed correctly" else @@ -238,22 +241,22 @@ test_help_command() { test_hash_generation() { print_test "Compose hash generation" - + # Create two identical compose files python3 "$VMM_CLI" compose \ --name test-hash-1 \ --docker-compose "$TEST_DIR/docker-compose.yml" \ --output "$TEST_DIR/hash1-output.json" > "$TEST_DIR/hash1.log" 2>&1 - + python3 "$VMM_CLI" compose \ --name test-hash-1 \ --docker-compose "$TEST_DIR/docker-compose.yml" \ --output "$TEST_DIR/hash2-output.json" > "$TEST_DIR/hash2.log" 2>&1 - + # Extract hashes from output HASH1=$(grep "Compose hash:" "$TEST_DIR/hash1.log" | cut -d' ' -f3) HASH2=$(grep "Compose hash:" "$TEST_DIR/hash2.log" | cut -d' ' -f3) - + if [[ "$HASH1" == "$HASH2" ]] && [[ -n "$HASH1" ]]; then print_success "Hash generation test passed - identical inputs produce identical hashes" else @@ -266,22 +269,22 @@ test_hash_generation() { main() { echo -e "${YELLOW}=== VMM-CLI Compose Subcommand Test Suite ===${NC}" echo "" - + # Check dependencies if ! command -v jq &> /dev/null; then echo -e "${RED}Error: jq is required for JSON testing but not installed${NC}" exit 1 fi - + if [[ ! -f "$VMM_CLI" ]]; then echo -e "${RED}Error: VMM CLI not found at $VMM_CLI${NC}" exit 1 fi - + # Setup test environment trap cleanup EXIT setup - + # Run tests (continue even if some fail) test_help_command || true test_basic_compose || true @@ -291,7 +294,7 @@ main() { test_prelaunch_script || true test_hash_generation || true test_error_handling || true - + # Results summary echo "" echo -e "${YELLOW}=== Test Results ===${NC}" @@ -305,4 +308,4 @@ main() { } # Run main function -main "$@" \ No newline at end of file +main "$@" diff --git a/vmm/src/tests/test-deployment.sh b/dstack/vmm/src/tests/test-deployment.sh similarity index 100% rename from vmm/src/tests/test-deployment.sh rename to dstack/vmm/src/tests/test-deployment.sh diff --git a/vmm/src/vmm-cli.py b/dstack/vmm/src/vmm-cli.py similarity index 100% rename from vmm/src/vmm-cli.py rename to dstack/vmm/src/vmm-cli.py diff --git a/vmm/src/x25519.js b/dstack/vmm/src/x25519.js similarity index 100% rename from vmm/src/x25519.js rename to dstack/vmm/src/x25519.js diff --git a/vmm/ui/.gitignore b/dstack/vmm/ui/.gitignore similarity index 100% rename from vmm/ui/.gitignore rename to dstack/vmm/ui/.gitignore diff --git a/vmm/ui/README.md b/dstack/vmm/ui/README.md similarity index 100% rename from vmm/ui/README.md rename to dstack/vmm/ui/README.md diff --git a/vmm/ui/build.mjs b/dstack/vmm/ui/build.mjs similarity index 100% rename from vmm/ui/build.mjs rename to dstack/vmm/ui/build.mjs diff --git a/vmm/ui/package-lock.json b/dstack/vmm/ui/package-lock.json similarity index 100% rename from vmm/ui/package-lock.json rename to dstack/vmm/ui/package-lock.json diff --git a/vmm/ui/package.json b/dstack/vmm/ui/package.json similarity index 100% rename from vmm/ui/package.json rename to dstack/vmm/ui/package.json diff --git a/vmm/ui/scripts/build_proto.sh b/dstack/vmm/ui/scripts/build_proto.sh similarity index 100% rename from vmm/ui/scripts/build_proto.sh rename to dstack/vmm/ui/scripts/build_proto.sh diff --git a/vmm/ui/src/App.ts b/dstack/vmm/ui/src/App.ts similarity index 100% rename from vmm/ui/src/App.ts rename to dstack/vmm/ui/src/App.ts diff --git a/vmm/ui/src/components/CreateVmDialog.ts b/dstack/vmm/ui/src/components/CreateVmDialog.ts similarity index 100% rename from vmm/ui/src/components/CreateVmDialog.ts rename to dstack/vmm/ui/src/components/CreateVmDialog.ts diff --git a/vmm/ui/src/components/EncryptedEnvEditor.ts b/dstack/vmm/ui/src/components/EncryptedEnvEditor.ts similarity index 100% rename from vmm/ui/src/components/EncryptedEnvEditor.ts rename to dstack/vmm/ui/src/components/EncryptedEnvEditor.ts diff --git a/vmm/ui/src/components/ForkVmDialog.ts b/dstack/vmm/ui/src/components/ForkVmDialog.ts similarity index 100% rename from vmm/ui/src/components/ForkVmDialog.ts rename to dstack/vmm/ui/src/components/ForkVmDialog.ts diff --git a/vmm/ui/src/components/GpuConfigEditor.ts b/dstack/vmm/ui/src/components/GpuConfigEditor.ts similarity index 100% rename from vmm/ui/src/components/GpuConfigEditor.ts rename to dstack/vmm/ui/src/components/GpuConfigEditor.ts diff --git a/vmm/ui/src/components/PortMappingEditor.ts b/dstack/vmm/ui/src/components/PortMappingEditor.ts similarity index 99% rename from vmm/ui/src/components/PortMappingEditor.ts rename to dstack/vmm/ui/src/components/PortMappingEditor.ts index 5fcf730a1..0668bd952 100644 --- a/vmm/ui/src/components/PortMappingEditor.ts +++ b/dstack/vmm/ui/src/components/PortMappingEditor.ts @@ -53,7 +53,7 @@ const PortMappingEditorComponent = { - /` (for example `os/mkosi/`) +without moving shared payload or duplicating release packaging. No mkosi +backend is implemented today. + +## Build + +For a first production build from a fresh checkout, use the repository-level +target: + +```bash +make os-image +``` + +It initializes only the Yocto dependency submodules and runs a complete build +inside the pinned builder container. See [Build the dstack guest OS](../docs/building-guest-os.md) +for prerequisites, output verification, flavor selection, reproducibility +checking, incremental development, and troubleshooting. + +The lower-level native backend interface remains available: + +```bash +make os-deps +./os/build.sh --backend yocto --build-dir "$PWD/os/yocto/bb-build" +``` + +`--flavors "prod dev"` selects image flavors. Native builds require the host +packages listed in `yocto/repro-build/Dockerfile.repro`. + +## Backend contract + +Each backend has two boundaries: + +1. `os//build.sh image [build-dir]` builds native artifacts. +2. The backend exports `os/spec/artifact-manifest.schema.json` version 1 and + calls `os/image/assemble.sh --manifest `. + +Artifact paths in a manifest are relative to the manifest. The common assembler +owns partitioned disk creation, release metadata, TDX/SNP/GCP measurement +material, checksums, and tarballs. Backends own how the kernel, initramfs, +firmware, dm-verity rootfs, and optional UKI are built. + +The Yocto compatibility entrypoint `os/yocto/mkimage.sh` demonstrates this +split: `scripts/export-artifacts.sh` handles BitBake paths and +`../image/assemble.sh` handles backend-independent packaging. + +## Source boundaries + +The guest recipe stages only the inputs it needs: + +- `dstack/` for core Rust services, +- `sdk/rust/` for public Rust SDK workspace members, +- `os/common/rootfs/` for OS-owned payload. + +It does not copy the entire repository or depend on a nested dstack submodule. + +## Licensing + +The dstack-owned core, SDK, documentation, tools, guest payload, Yocto backend, +and image-assembly code are Apache-2.0. Individual vendored recipes, patches, +and embedded components may carry their own notices; moving them into this +repository does not relicense them. diff --git a/os/build.sh b/os/build.sh new file mode 100755 index 000000000..63254982b --- /dev/null +++ b/os/build.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# SPDX-FileCopyrightText: © 2026 Phala Network +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +usage() { + cat </build.sh and implements the "image" action. +The common image contract is documented in os/README.md. +EOF +} + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +BACKEND=yocto +FLAVORS=${FLAVORS:-prod} +BUILD_DIR= + +while [ $# -gt 0 ]; do + case "$1" in + --backend) + [ $# -ge 2 ] || { echo "Error: --backend requires a value" >&2; exit 1; } + BACKEND=$2 + shift 2 + ;; + --flavors) + [ $# -ge 2 ] || { echo "Error: --flavors requires a value" >&2; exit 1; } + FLAVORS=$2 + shift 2 + ;; + --build-dir) + [ $# -ge 2 ] || { echo "Error: --build-dir requires a value" >&2; exit 1; } + BUILD_DIR=$2 + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +case "$BACKEND" in + ''|*[!0-9A-Za-z_-]*) + echo "Error: invalid OS backend name: $BACKEND" >&2 + exit 1 + ;; +esac + +BACKEND_SCRIPT="$SCRIPT_DIR/$BACKEND/build.sh" +if [ ! -x "$BACKEND_SCRIPT" ]; then + echo "Error: OS backend is not available: $BACKEND" >&2 + echo "Expected executable: $BACKEND_SCRIPT" >&2 + exit 1 +fi + +export FLAVORS +if [ -n "$BUILD_DIR" ]; then + exec "$BACKEND_SCRIPT" image "$BUILD_DIR" +else + exec "$BACKEND_SCRIPT" image +fi diff --git a/os/common/README.md b/os/common/README.md new file mode 100644 index 000000000..32088a905 --- /dev/null +++ b/os/common/README.md @@ -0,0 +1,11 @@ +# Common guest rootfs payload + +`rootfs/` contains files installed into every dstack guest independently of the +OS build backend. It is intentionally separate from Rust application source and +from Yocto recipes. + +A backend should stage these files into their documented destinations. The +current authoritative mapping is in +`../yocto/layers/meta-dstack/recipes-core/dstack-guest/dstack-guest.bb`. When a +second backend is introduced, keep the payload canonical here and add a +backend-specific installer rather than copying the files. diff --git a/basefiles/app-compose.service b/os/common/rootfs/app-compose.service similarity index 100% rename from basefiles/app-compose.service rename to os/common/rootfs/app-compose.service diff --git a/basefiles/app-compose.sh b/os/common/rootfs/app-compose.sh old mode 100644 new mode 100755 similarity index 93% rename from basefiles/app-compose.sh rename to os/common/rootfs/app-compose.sh index 0387ed348..ea5ed9de0 --- a/basefiles/app-compose.sh +++ b/os/common/rootfs/app-compose.sh @@ -9,9 +9,10 @@ SYS_CONFIG_FILE="$HOST_SHARED_DIR/.sys-config.json" CFG_PCCS_URL=$([ -f "$SYS_CONFIG_FILE" ] && jq -r '.pccs_url//""' "$SYS_CONFIG_FILE" || echo "") export PCCS_URL=${PCCS_URL:-$CFG_PCCS_URL} -if [ $(jq 'has("pre_launch_script")' app-compose.json) == true ]; then +if [ "$(jq 'has("pre_launch_script")' app-compose.json)" = true ]; then echo "Running pre-launch script" dstack-util notify-host -e "boot.progress" -d "pre-launch" || true + # shellcheck disable=SC1090 source <(jq -r '.pre_launch_script' app-compose.json) fi diff --git a/basefiles/containerd.service.d/dstack-prepare.conf b/os/common/rootfs/containerd.service.d/dstack-prepare.conf similarity index 100% rename from basefiles/containerd.service.d/dstack-prepare.conf rename to os/common/rootfs/containerd.service.d/dstack-prepare.conf diff --git a/basefiles/docker.service.d/dstack-guest-agent.conf b/os/common/rootfs/docker.service.d/dstack-guest-agent.conf similarity index 100% rename from basefiles/docker.service.d/dstack-guest-agent.conf rename to os/common/rootfs/docker.service.d/dstack-guest-agent.conf diff --git a/basefiles/docker.service.d/dstack-prepare.conf b/os/common/rootfs/docker.service.d/dstack-prepare.conf similarity index 100% rename from basefiles/docker.service.d/dstack-prepare.conf rename to os/common/rootfs/docker.service.d/dstack-prepare.conf diff --git a/basefiles/dstack-guest-agent.service b/os/common/rootfs/dstack-guest-agent.service similarity index 100% rename from basefiles/dstack-guest-agent.service rename to os/common/rootfs/dstack-guest-agent.service diff --git a/basefiles/dstack-guest-agent.socket b/os/common/rootfs/dstack-guest-agent.socket similarity index 100% rename from basefiles/dstack-guest-agent.socket rename to os/common/rootfs/dstack-guest-agent.socket diff --git a/basefiles/dstack-prepare.service b/os/common/rootfs/dstack-prepare.service similarity index 100% rename from basefiles/dstack-prepare.service rename to os/common/rootfs/dstack-prepare.service diff --git a/basefiles/dstack-prepare.sh b/os/common/rootfs/dstack-prepare.sh similarity index 100% rename from basefiles/dstack-prepare.sh rename to os/common/rootfs/dstack-prepare.sh diff --git a/basefiles/ephemeral-docker.sh b/os/common/rootfs/ephemeral-docker.sh similarity index 95% rename from basefiles/ephemeral-docker.sh rename to os/common/rootfs/ephemeral-docker.sh index 9f568eab9..40c596ef2 100755 --- a/basefiles/ephemeral-docker.sh +++ b/os/common/rootfs/ephemeral-docker.sh @@ -28,7 +28,7 @@ cleanup() { if [ -n "$DOCKERD_PID" ] && kill -0 $DOCKERD_PID 2>/dev/null; then kill -TERM $DOCKERD_PID 2>/dev/null # Wait with timeout - for i in $(seq 1 50); do + for _ in $(seq 1 50); do kill -0 $DOCKERD_PID 2>/dev/null || break sleep 0.1 done @@ -40,7 +40,7 @@ cleanup() { # Stop containerd if [ -n "$CONTAINERD_PID" ] && kill -0 $CONTAINERD_PID 2>/dev/null; then kill -TERM $CONTAINERD_PID 2>/dev/null - for i in $(seq 1 50); do + for _ in $(seq 1 50); do kill -0 $CONTAINERD_PID 2>/dev/null || break sleep 0.1 done @@ -50,7 +50,7 @@ cleanup() { # Unmount any netns that docker created if [ -d "$TMPDIR/docker-exec/netns" ]; then - find "$TMPDIR/docker-exec/netns" -type f 2>/dev/null | while read ns; do + find "$TMPDIR/docker-exec/netns" -type f 2>/dev/null | while read -r ns; do umount "$ns" 2>/dev/null || true done fi @@ -75,7 +75,7 @@ CONTAINERD_PID=$! # Wait for containerd socket with timeout TIMEOUT=100 # 10 seconds -for i in $(seq 1 $TIMEOUT); do +for _ in $(seq 1 "$TIMEOUT"); do [ -S "$TMPDIR/containerd.sock" ] && break if ! kill -0 $CONTAINERD_PID 2>/dev/null; then echo "Error: containerd exited unexpectedly" >&2 @@ -99,7 +99,7 @@ dockerd \ DOCKERD_PID=$! # Wait for docker socket with timeout -for i in $(seq 1 $TIMEOUT); do +for _ in $(seq 1 "$TIMEOUT"); do [ -S "$TMPDIR/docker.sock" ] && break if ! kill -0 $DOCKERD_PID 2>/dev/null; then echo "Error: dockerd exited unexpectedly" >&2 diff --git a/basefiles/journald.conf b/os/common/rootfs/journald.conf similarity index 100% rename from basefiles/journald.conf rename to os/common/rootfs/journald.conf diff --git a/basefiles/llmnr.conf b/os/common/rootfs/llmnr.conf similarity index 52% rename from basefiles/llmnr.conf rename to os/common/rootfs/llmnr.conf index 4b0ccf845..ad4ea0b28 100644 --- a/basefiles/llmnr.conf +++ b/os/common/rootfs/llmnr.conf @@ -1,2 +1,2 @@ [Resolve] -LLMNR=no \ No newline at end of file +LLMNR=no diff --git a/basefiles/sysctl.d/99-dstack.conf b/os/common/rootfs/sysctl.d/99-dstack.conf similarity index 100% rename from basefiles/sysctl.d/99-dstack.conf rename to os/common/rootfs/sysctl.d/99-dstack.conf diff --git a/os/common/rootfs/tdx-attest.conf b/os/common/rootfs/tdx-attest.conf new file mode 100644 index 000000000..aa6ae961e --- /dev/null +++ b/os/common/rootfs/tdx-attest.conf @@ -0,0 +1 @@ +port=4050 diff --git a/basefiles/wg-checker.service b/os/common/rootfs/wg-checker.service similarity index 100% rename from basefiles/wg-checker.service rename to os/common/rootfs/wg-checker.service diff --git a/basefiles/wg-checker.sh b/os/common/rootfs/wg-checker.sh similarity index 100% rename from basefiles/wg-checker.sh rename to os/common/rootfs/wg-checker.sh diff --git a/os/image/README.md b/os/image/README.md new file mode 100644 index 000000000..3215939ea --- /dev/null +++ b/os/image/README.md @@ -0,0 +1,19 @@ +# Common image assembly + +`assemble.sh` consumes the versioned manifest in +`../spec/artifact-manifest.schema.json`. It must not inspect a backend build +tree or invoke backend-specific tools such as BitBake. + +The assembler creates the partitioned rootfs, metadata, measurement CBOR files, +unified digest, and release archives. Backend exporters may use symlinks for +large local artifacts; all paths recorded in the manifest itself are relative +to the manifest directory. + +`mk-image-mr.sh ` creates the flattened, rootfs-free +`mr_.tar.gz` bundle consumed by verifier/KMS image-download endpoints. +Because this is release-format post-processing rather than a Yocto operation, +the helper lives beside the common assembler. + +`dstack-image-oci.sh` pushes and lists assembled guest-image directories in an +OCI registry. It is likewise independent of the backend that produced the +image. diff --git a/os/image/assemble.sh b/os/image/assemble.sh new file mode 100755 index 000000000..fdf66a7f3 --- /dev/null +++ b/os/image/assemble.sh @@ -0,0 +1,472 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +DSTACK_TAR_RELEASE=${DSTACK_TAR_RELEASE:-1} +ENABLE_UKI_IMAGE=${ENABLE_UKI_IMAGE:-1} + +# Fixed GPT GUIDs so partitioned images are reproducible (sgdisk randomizes by +# default). Partitions are located by PARTLABEL, not GUID, so these are arbitrary. +DSTACK_DISK_GUID=${DSTACK_DISK_GUID:-d5acc000-0000-4000-8000-000000000000} +DSTACK_ROOTFS_PART_GUID=${DSTACK_ROOTFS_PART_GUID:-d5acc000-0000-4000-8000-000000000001} +DSTACK_EFI_PART_GUID=${DSTACK_EFI_PART_GUID:-d5acc000-0000-4000-8000-000000000002} + +usage() { + cat </dist) + OUTPUT_DIR Unpacked release directory + DSTACK_TAR_RELEASE Create release tarballs (default: 1) + ENABLE_UKI_IMAGE Create the optional UKI disk image (default: 1) + DSTACK_MR_BIN Existing dstack-mr binary +EOF +} + +MANIFEST= +while [ $# -gt 0 ]; do + case "$1" in + --manifest) + MANIFEST=$2 + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [ -z "$MANIFEST" ]; then + echo "Error: --manifest is required" >&2 + usage >&2 + exit 1 +fi +if [ ! -f "$MANIFEST" ]; then + echo "Error: artifact manifest not found: $MANIFEST" >&2 + exit 1 +fi +MANIFEST=$(realpath "$MANIFEST") + +# Read and validate the small contract without requiring jq or jsonschema. +mapfile -d '' -t MANIFEST_VALUES < <( + python3 - "$MANIFEST" <<'PYMANIFEST' +import json +import os +import sys + +manifest_path = os.path.realpath(sys.argv[1]) +with open(manifest_path, encoding="utf-8") as file: + data = json.load(file) + +if data.get("schema_version") != 1: + raise SystemExit("unsupported artifact manifest schema_version") +if not isinstance(data.get("image", {}).get("is_dev"), bool): + raise SystemExit("image.is_dev must be a boolean") + +base = os.path.dirname(manifest_path) + +def required(obj, *keys): + value = obj + for key in keys: + if not isinstance(value, dict) or key not in value: + raise SystemExit("missing manifest field: " + ".".join(keys)) + value = value[key] + if value is None or value == "": + raise SystemExit("empty manifest field: " + ".".join(keys)) + return value + +def artifact(name, optional=False): + value = data.get("artifacts", {}).get(name) + if value in (None, ""): + if optional: + return "" + raise SystemExit(f"missing manifest artifact: {name}") + if not isinstance(value, str): + raise SystemExit(f"artifact path must be a string: {name}") + normalized = os.path.normpath(value) + if os.path.isabs(value) or normalized == ".." or normalized.startswith("../"): + raise SystemExit(f"artifact path must be relative to the manifest: {name}") + path = os.path.realpath(os.path.join(base, value)) + if not os.path.isfile(path): + raise SystemExit(f"artifact does not exist: {name}: {path}") + return path + +values = [ + required(data, "backend"), + required(data, "image", "name"), + required(data, "image", "version"), + required(data, "image", "flavor"), + "true" if data["image"]["is_dev"] else "false", + required(data, "source", "git_revision"), + required(data, "boot", "ovmf_variant"), + required(data, "verity", "root_hash"), + str(required(data, "verity", "data_size")), + artifact("initramfs"), + artifact("kernel"), + artifact("firmware"), + artifact("rootfs_verity"), + artifact("firmware_sev", optional=True), + artifact("uki", optional=True), +] +for value in values: + if not isinstance(value, str): + raise SystemExit("manifest scalar fields must be strings") + sys.stdout.buffer.write(value.encode() + b"\0") +PYMANIFEST +) + +if [ "${#MANIFEST_VALUES[@]}" -ne 15 ]; then + echo "Error: failed to read artifact manifest: $MANIFEST" >&2 + exit 1 +fi + +BACKEND=${MANIFEST_VALUES[0]} +DIST_NAME=${MANIFEST_VALUES[1]} +DSTACK_VERSION=${MANIFEST_VALUES[2]} +FLAVOR=${MANIFEST_VALUES[3]} +IS_DEV=${MANIFEST_VALUES[4]} +GIT_REVISION=${MANIFEST_VALUES[5]} +OVMF_VARIANT=${MANIFEST_VALUES[6]} +ROOT_HASH=${MANIFEST_VALUES[7]} +DATA_SIZE=${MANIFEST_VALUES[8]} +INITRAMFS_IMAGE=${MANIFEST_VALUES[9]} +KERNEL_IMAGE=${MANIFEST_VALUES[10]} +OVMF_FIRMWARE=${MANIFEST_VALUES[11]} +ROOTFS_IMAGE=${MANIFEST_VALUES[12]} +OVMF_SEV_FIRMWARE=${MANIFEST_VALUES[13]} +UKI_IMAGE=${MANIFEST_VALUES[14]} + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(realpath "$SCRIPT_DIR/../..") +AUTHENTICODE_HASH_SCRIPT="${SCRIPT_DIR}/authenticode_hash.py" + +MANIFEST_DIR=$(dirname "$MANIFEST") +DIST_DIR=${DIST_DIR:-"${MANIFEST_DIR}/dist"} +mkdir -p "$DIST_DIR" +DIST_DIR=$(realpath "$DIST_DIR") +OUTPUT_DIR=${OUTPUT_DIR:-"${DIST_DIR}/${DIST_NAME}-${DSTACK_VERSION}"} +OUTPUT_DIR=$(realpath -m "$OUTPUT_DIR") +case "$OUTPUT_DIR" in + /|"$DIST_DIR"|"$MANIFEST_DIR"|"$REPO_ROOT") + echo "Error: refusing unsafe output directory: $OUTPUT_DIR" >&2 + exit 1 + ;; +esac +IMAGE_TAR=${IMAGE_TAR:-"${DIST_DIR}/${DIST_NAME}-${DSTACK_VERSION}.tar.gz"} +IMAGE_TAR_UKI=${IMAGE_TAR_UKI:-"${DIST_DIR}/${DIST_NAME}-${DSTACK_VERSION}-uki.tar.gz"} +TAR_DIR_NAME=$(basename "$OUTPUT_DIR") + +echo "Assembling ${DIST_NAME} ${DSTACK_VERSION} from ${BACKEND} artifacts (${FLAVOR})" + +verbose() { + printf '+ ' + printf '%q ' "$@" + printf '\n' + "$@" +} + +align_up() { + local value=$1 + local align=$2 + echo $(( ( (value + align - 1) / align ) * align )) +} + +calc_authenticode_hash() { + local file="$1" + + if [[ ! -f "$AUTHENTICODE_HASH_SCRIPT" ]] || ! command -v python3 &>/dev/null; then + return 0 + fi + + python3 "$AUTHENTICODE_HASH_SCRIPT" "$file" 2>/dev/null || true +} + +write_authenticode_hash() { + local file="$1" + local out_file="$2" + + if [[ ! -f "$file" ]]; then + return 0 + fi + + if [[ ! -f "$AUTHENTICODE_HASH_SCRIPT" ]] || ! command -v python3 &>/dev/null; then + echo "Warning: authenticode_hash.py not found or python3 not available, skipping Authenticode hash calculation" >&2 + return 0 + fi + + echo "Calculating UKI Authenticode hash..." + local auth_hash + auth_hash=$(calc_authenticode_hash "$file") + if [[ -n "$auth_hash" ]]; then + echo "$auth_hash" > "$out_file" + echo "UKI Authenticode hash: $auth_hash" + else + echo "Warning: Failed to calculate UKI Authenticode hash" >&2 + fi +} + +ensure_dstack_mr() { + DSTACK_SRC="${DSTACK_SRC:-$REPO_ROOT/dstack}" + if [ -z "${DSTACK_MR_BIN:-}" ] && [ -x "$DSTACK_SRC/target/release/dstack-mr" ]; then + DSTACK_MR_BIN="$DSTACK_SRC/target/release/dstack-mr" + fi + if [ -z "${DSTACK_MR_BIN:-}" ]; then + echo "Building dstack-mr to compute OS-image measurement material" + ( cd "$DSTACK_SRC" && cargo build --release -p dstack-mr ) + DSTACK_MR_BIN="$DSTACK_SRC/target/release/dstack-mr" + fi +} + +create_partitioned_rootfs() { + local rootfs_img="$1" + local output_img="$2" + ( + set -e + local align=$((1024 * 1024)) + local sector=512 + local rootfs_size + local rootfs_size_aligned + local total_size + rootfs_size=$(stat -L -c %s "$rootfs_img") + rootfs_size_aligned=$(align_up "$rootfs_size" "$align") + local rootfs_start=$align + # Leave extra room for GPT headers (1MB at start, 1MB at end) + total_size=$(align_up $((rootfs_start + rootfs_size_aligned + align)) "$align") + + truncate -s "$total_size" "$output_img" + + local root_start_sector=$((rootfs_start / sector)) + local root_end_sector=$((root_start_sector + (rootfs_size_aligned / sector) - 1)) + + sgdisk --zap-all "$output_img" >/dev/null + # Fixed GUIDs keep the image bit-for-bit reproducible (GPT otherwise + # randomizes disk/partition GUIDs). The rootfs is located by PARTLABEL. + sgdisk --disk-guid="${DSTACK_DISK_GUID}" \ + --new=1:${root_start_sector}:${root_end_sector} --typecode=1:8300 \ + --partition-guid=1:"${DSTACK_ROOTFS_PART_GUID}" \ + --change-name=1:'dstack-rootfs' "$output_img" >/dev/null + + dd if="$rootfs_img" of="$output_img" bs=$align seek=$((rootfs_start / align)) conv=notrunc status=none + ) +} + +build_uki_disk_image() { + local disk_img="$1" + local uki_file="$2" + local rootfs_img="$3" + ( + set -e + local align=$((1024 * 1024)) + local sector=512 + local efi_size=$((256 * 1024 * 1024)) + local efi_size_aligned + local rootfs_size + local rootfs_size_aligned + local total_size + efi_size_aligned=$(align_up "$efi_size" "$align") + rootfs_size=$(stat -L -c %s "$rootfs_img") + rootfs_size_aligned=$(align_up "$rootfs_size" "$align") + local efi_start=$align + local rootfs_start=$((efi_start + efi_size_aligned)) + # Leave extra room for the backup GPT header + total_size=$(align_up $((rootfs_start + rootfs_size_aligned + align)) "$align") + + truncate -s "$total_size" "$disk_img" + + local efi_start_sector=$((efi_start / sector)) + local efi_end_sector=$((efi_start_sector + (efi_size_aligned / sector) - 1)) + local root_start_sector=$((rootfs_start / sector)) + local root_end_sector=$((root_start_sector + (rootfs_size_aligned / sector) - 1)) + + sgdisk --zap-all "$disk_img" >/dev/null + sgdisk --disk-guid="${DSTACK_DISK_GUID}" \ + --new=1:${efi_start_sector}:${efi_end_sector} --typecode=1:ef00 \ + --partition-guid=1:"${DSTACK_EFI_PART_GUID}" \ + --change-name=1:'EFI System Partition' "$disk_img" >/dev/null + sgdisk --new=2:${root_start_sector}:${root_end_sector} --typecode=2:8300 \ + --partition-guid=2:"${DSTACK_ROOTFS_PART_GUID}" \ + --change-name=2:'dstack-rootfs' "$disk_img" >/dev/null + + local tmp_dir + tmp_dir=$(mktemp -d) + trap 'rm -rf "$tmp_dir"' EXIT + + # Create EFI filesystem with UKI as bootloader + local efi_img=${tmp_dir}/efi.img + mkfs.vfat -F 32 -n DSTACKEFI -C "$efi_img" $((efi_size_aligned / 1024)) >/dev/null + mmd -i "$efi_img" ::EFI ::EFI/BOOT + mcopy -i "$efi_img" "$uki_file" ::EFI/BOOT/BOOTX64.EFI + + dd if="$efi_img" of="$disk_img" bs=$align seek=$((efi_start / align)) conv=notrunc status=none + dd if="$rootfs_img" of="$disk_img" bs=$align seek=$((rootfs_start / align)) conv=notrunc status=none + ) +} + +create_uki_artifacts() { + local uki_dir="$1" + mkdir -p "$uki_dir" + + echo "Building UKI disk image at ${uki_dir}/disk.raw" + build_uki_disk_image "${uki_dir}/disk.raw" "$UKI_IMAGE" "$ROOTFS_IMAGE" + + write_authenticode_hash "$UKI_IMAGE" "${uki_dir}/auth_hash.txt" +} + +# Create bare metal image directory +verbose rm -rf "${OUTPUT_DIR}/" +verbose mkdir -p "${OUTPUT_DIR}/" +verbose cp "$INITRAMFS_IMAGE" "${OUTPUT_DIR}/initramfs.cpio.gz" +verbose cp "$KERNEL_IMAGE" "${OUTPUT_DIR}/bzImage" +verbose cp "$OVMF_FIRMWARE" "${OUTPUT_DIR}/ovmf.fd" + +# AMD SEV firmware (additive). Shipped alongside the TDX firmware so a SEV-SNP +# launch can select it via the metadata.json "bios-sev" field below. The SEV +# firmware blob itself is NOT added directly to sha256sum.txt; when present, its +# OVMF hash/sections are committed by measurement.snp.cbor, and that file is +# part of digest.txt. This does not change any TDX hardware +# measurement (MRTD comes from ovmf.fd, RTMRs from kernel/cmdline/rootfs) -- it +# only changes dstack's image-bundle digest. +HAVE_OVMF_SEV=0 +BIOS_SEV_JSON="" +if [ -n "$OVMF_SEV_FIRMWARE" ]; then + verbose cp "$OVMF_SEV_FIRMWARE" "${OUTPUT_DIR}/ovmf-sev.fd" + HAVE_OVMF_SEV=1 + # Inserted after the "bios" line in metadata.json (see below). + BIOS_SEV_JSON=' + "bios-sev": "ovmf-sev.fd",' +fi + +echo "Creating partitioned rootfs image at ${OUTPUT_DIR}/rootfs.img.parted.verity" +# Bare-metal partitioning needs sgdisk (from the 'gdisk' package). +if ! command -v sgdisk >/dev/null; then + echo "Error: cannot create partitioned rootfs image because 'sgdisk' is missing; install 'gdisk'." >&2 + exit 1 +fi +create_partitioned_rootfs "$ROOTFS_IMAGE" "${OUTPUT_DIR}/rootfs.img.parted.verity" + +echo "Generating metadata.json to ${OUTPUT_DIR}/metadata.json (ovmf_variant=$OVMF_VARIANT)" + +KARG0="console=ttyS0 init=/init panic=1 net.ifnames=0 biosdevname=0" +KARG1="mce=off oops=panic pci=noearly pci=nommconf random.trust_cpu=y random.trust_bootloader=n tsc=reliable no-kvmclock" +KARG2="dstack.rootfs_hash=$ROOT_HASH dstack.rootfs_size=$DATA_SIZE" + +cat < "${OUTPUT_DIR}/metadata.json" +{ + "bios": "ovmf.fd",${BIOS_SEV_JSON} + "kernel": "bzImage", + "cmdline": "$KARG0 $KARG1 $KARG2", + "initrd": "initramfs.cpio.gz", + "rootfs": "rootfs.img.parted.verity", + "version": "$DSTACK_VERSION", + "git_revision": "$GIT_REVISION", + "shared_ro": true, + "is_dev": ${IS_DEV}, + "ovmf_variant": "$OVMF_VARIANT" +} +EOF + +ensure_dstack_mr + +echo "Generating measurement.tdx.cbor via ${DSTACK_MR_BIN}" +"${DSTACK_MR_BIN}" tdx-measurement-cbor "${OUTPUT_DIR}" > "${OUTPUT_DIR}/measurement.tdx.cbor" + +HAVE_MEASUREMENT_SNP=0 +if [ "$HAVE_OVMF_SEV" = "1" ]; then + echo "Generating measurement.snp.cbor via ${DSTACK_MR_BIN}" + "${DSTACK_MR_BIN}" snp-measurement-cbor "${OUTPUT_DIR}" > "${OUTPUT_DIR}/measurement.snp.cbor" + HAVE_MEASUREMENT_SNP=1 +fi + +# Create UKI artifacts (disk.raw and auth_hash.txt) in OUTPUT_DIR +UKI_CREATED=0 +if [ "$ENABLE_UKI_IMAGE" = "1" ]; then + if [[ -z "$UKI_IMAGE" ]]; then + echo "Skipping UKI disk image creation because the backend did not export a UKI" >&2 + elif command -v sgdisk >/dev/null && \ + command -v mkfs.vfat >/dev/null && \ + command -v mcopy >/dev/null && \ + command -v mmd >/dev/null; then + create_uki_artifacts "${OUTPUT_DIR}" + UKI_CREATED=1 + else + echo "Error: cannot create UKI disk image because required tools are missing" >&2 + echo "Missing tools are among: sgdisk (gdisk), mkfs.vfat (dosfstools), mcopy/mmd (mtools)" >&2 + echo "Install them (e.g. apt-get install -y gdisk dosfstools mtools) or set ENABLE_UKI_IMAGE=0" >&2 + exit 1 + fi +fi + +HAVE_MEASUREMENT_GCP=0 +if [[ "$UKI_CREATED" = "1" ]]; then + if [[ ! -f "${OUTPUT_DIR}/auth_hash.txt" ]]; then + echo "Error: UKI image was created but auth_hash.txt is missing" >&2 + exit 1 + fi + echo "Generating measurement.gcp.cbor via ${DSTACK_MR_BIN}" + "${DSTACK_MR_BIN}" gcp-measurement-cbor "${OUTPUT_DIR}/auth_hash.txt" > "${OUTPUT_DIR}/measurement.gcp.cbor" + HAVE_MEASUREMENT_GCP=1 +fi + +echo "Generating unified image digest to ${OUTPUT_DIR}/" +CHECKSUM_FILES=(ovmf.fd bzImage initramfs.cpio.gz metadata.json measurement.tdx.cbor) +if [ "$HAVE_MEASUREMENT_SNP" = "1" ]; then + CHECKSUM_FILES+=(measurement.snp.cbor) +fi +if [ "$HAVE_MEASUREMENT_GCP" = "1" ]; then + CHECKSUM_FILES+=(measurement.gcp.cbor) +fi +( + cd "${OUTPUT_DIR}/" + sha256sum "${CHECKSUM_FILES[@]}" > sha256sum.txt + sha256sum sha256sum.txt | awk '{print $1}' > digest.txt +) + +if [ "$DSTACK_TAR_RELEASE" = "1" ]; then + OUTPUT_DIR=$(realpath "${OUTPUT_DIR}") + PARENT_DIR=$(dirname "${OUTPUT_DIR}") + + # Bare metal tarball: all files except disk.raw and auth_hash.txt + rm -rf "${IMAGE_TAR}" + echo "Archiving bare metal image to ${IMAGE_TAR}" + BARE_METAL_FILES=(rootfs.img.parted.verity bzImage ovmf.fd digest.txt sha256sum.txt initramfs.cpio.gz metadata.json measurement.tdx.cbor) + if [ "$HAVE_OVMF_SEV" = "1" ]; then + BARE_METAL_FILES+=(ovmf-sev.fd) + fi + if [ "$HAVE_MEASUREMENT_SNP" = "1" ]; then + BARE_METAL_FILES+=(measurement.snp.cbor) + fi + if [ "$HAVE_MEASUREMENT_GCP" = "1" ]; then + BARE_METAL_FILES+=(measurement.gcp.cbor) + fi + BARE_METAL_TAR_FILES=() + for file in "${BARE_METAL_FILES[@]}"; do + BARE_METAL_TAR_FILES+=("$TAR_DIR_NAME/$file") + done + (cd "$PARENT_DIR" && tar -czvf "$IMAGE_TAR" "${BARE_METAL_TAR_FILES[@]}") + echo + + # UKI tarball: GCP boot disk plus the unified OS-image identity material. + if [[ "$UKI_CREATED" = "1" ]]; then + rm -rf "${IMAGE_TAR_UKI}" + echo "Archiving UKI image to ${IMAGE_TAR_UKI}" + UKI_FILES=(disk.raw digest.txt sha256sum.txt measurement.gcp.cbor) + UKI_TAR_FILES=() + for file in "${UKI_FILES[@]}"; do + UKI_TAR_FILES+=("$TAR_DIR_NAME/$file") + done + (cd "$PARENT_DIR" && tar -czvf "$IMAGE_TAR_UKI" "${UKI_TAR_FILES[@]}") + echo + fi +fi diff --git a/os/image/authenticode_hash.py b/os/image/authenticode_hash.py new file mode 100644 index 000000000..c3fa3f484 --- /dev/null +++ b/os/image/authenticode_hash.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +"""Calculate the Authenticode SHA-256 hash of a PE/COFF image.""" + +import argparse +import hashlib + + +def read_le_u16(data: bytes, offset: int) -> int: + """Read an unsigned little-endian 16-bit integer.""" + import struct + + return struct.unpack(" int: + """Read an unsigned little-endian 32-bit integer.""" + import struct + + return struct.unpack(" str: + """Return the PE/COFF Authenticode SHA-256 digest for *filepath*.""" + with open(filepath, "rb") as f: + data = f.read() + + # Read DOS header + lfanew_offset = 0x3C + lfanew = read_le_u32(data, lfanew_offset) + + # Verify PE signature + pe_sig_offset = lfanew + pe_sig = read_le_u32(data, pe_sig_offset) + IMAGE_NT_SIGNATURE = 0x00004550 # "PE\0\0" + if pe_sig != IMAGE_NT_SIGNATURE: + raise ValueError(f"Invalid PE signature in {filepath}") + + # Read COFF header + coff_header_offset = pe_sig_offset + 4 + optional_header_size = read_le_u16(data, coff_header_offset + 16) + + # Read Optional header magic + optional_header_offset = coff_header_offset + 20 + magic = read_le_u16(data, optional_header_offset) + + is_pe32_plus = magic == 0x20B + + # Calculate offsets for excluded regions (checksum and cert directory) + checksum_offset = optional_header_offset + 64 + checksum_end = checksum_offset + 4 + + data_dir_offset = optional_header_offset + (112 if is_pe32_plus else 96) + IMAGE_DIRECTORY_ENTRY_SECURITY = 4 + cert_dir_offset = data_dir_offset + (IMAGE_DIRECTORY_ENTRY_SECURITY * 8) + cert_dir_end = cert_dir_offset + 8 + + size_of_headers_offset = optional_header_offset + 60 + size_of_headers = read_le_u32(data, size_of_headers_offset) + + # Hash header (excluding checksum and cert directory) + hasher = hashlib.sha256() + hasher.update(data[0:checksum_offset]) + hasher.update(data[checksum_end:cert_dir_offset]) + hasher.update(data[cert_dir_end:size_of_headers]) + + sum_of_bytes_hashed = size_of_headers + + # Read section table + num_sections_offset = coff_header_offset + 2 + num_sections = read_le_u16(data, num_sections_offset) + + section_table_offset = optional_header_offset + optional_header_size + section_size = 40 + + sections = [] + for i in range(num_sections): + section_offset = section_table_offset + (i * section_size) + + ptr_raw_data_offset = section_offset + 20 + ptr_raw_data = read_le_u32(data, ptr_raw_data_offset) + + size_raw_data_offset = section_offset + 16 + size_raw_data = read_le_u32(data, size_raw_data_offset) + + if size_raw_data > 0: + sections.append((ptr_raw_data, size_raw_data)) + + # Sort sections by offset + sections.sort(key=lambda x: x[0]) + + # Hash sections + for offset, size in sections: + start = offset + end = start + size + + if end <= len(data): + hasher.update(data[start:end]) + else: + available_size = max(0, len(data) - start) + if available_size > 0: + hasher.update(data[start : start + available_size]) + + sum_of_bytes_hashed += size + + file_size = len(data) + + # Read certificate table info + cert_table_addr = read_le_u32(data, cert_dir_offset) + cert_table_size = read_le_u32(data, cert_dir_offset + 4) + + # Hash trailing data (excluding certificate table) + if cert_table_addr > 0 and cert_table_size > 0 and file_size > sum_of_bytes_hashed: + trailing_data_len = file_size - sum_of_bytes_hashed + + if trailing_data_len > cert_table_size: + hashed_trailing_len = trailing_data_len - cert_table_size + trailing_start = sum_of_bytes_hashed + + if trailing_start + hashed_trailing_len <= len(data): + hasher.update( + data[trailing_start : trailing_start + hashed_trailing_len] + ) + + # Add padding to align to 8 bytes + remainder = file_size % 8 + if remainder != 0: + padding = bytes([0] * (8 - remainder)) + hasher.update(padding) + + return hasher.hexdigest() + + +def main() -> None: + """Run the command-line interface.""" + parser = argparse.ArgumentParser( + description="Calculate PE/COFF Authenticode SHA256 hash (TPM Event Log compatible)" + ) + parser.add_argument("file", help="Path to PE/COFF binary (e.g., UKI .efi)") + args = parser.parse_args() + + print(authenticode_hash(args.file)) + + +if __name__ == "__main__": + main() diff --git a/scripts/dstack-image-oci.sh b/os/image/dstack-image-oci.sh similarity index 76% rename from scripts/dstack-image-oci.sh rename to os/image/dstack-image-oci.sh index f8b986224..580d6e334 100755 --- a/scripts/dstack-image-oci.sh +++ b/os/image/dstack-image-oci.sh @@ -7,6 +7,7 @@ set -euo pipefail usage() { + local status=${1:-1} cat < [options] @@ -19,12 +20,12 @@ Arguments: Full image reference (e.g., ghcr.io/org/guest-image) Examples: - $0 push ./dstack-0.5.8 cr.kvin.wang/dstack/guest-image - $0 push ./dstack-nvidia-0.5.8 ghcr.io/dstack-tee/guest-image --tag nvidia-0.5.8 - $0 list cr.kvin.wang/dstack/guest-image - $0 list cr.kvin.wang/dstack/guest-image --filter nvidia + $0 push ./dstack-0.6.0 ghcr.io/dstack-tee/guest-image + $0 push ./dstack-0.6.0 ghcr.io/dstack-tee/guest-image --tag 0.6.0 + $0 list ghcr.io/dstack-tee/guest-image + $0 list ghcr.io/dstack-tee/guest-image --filter nvidia EOF - exit 1 + exit "$status" } COMMAND="${1:-}" @@ -32,15 +33,19 @@ COMMAND="${1:-}" shift # --- PUSH --- -cmd_push() { +cmd_push() ( local image_dir="" local image_ref="" local extra_tag="" while [ $# -gt 0 ]; do case "$1" in - --tag) extra_tag="$2"; shift 2 ;; - -h|--help) usage ;; + --tag) + [ $# -ge 2 ] || { echo "Error: --tag requires a value"; exit 1; } + extra_tag="$2" + shift 2 + ;; + -h|--help) usage 0 ;; -*) echo "Unknown option: $1"; exit 1 ;; *) if [ -z "$image_dir" ]; then @@ -64,11 +69,27 @@ cmd_push() { # Read image info local version - version=$(python3 -c "import json; print(json.load(open('$metadata'))['version'])") + version=$(python3 - "$metadata" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as file: + print(json.load(file)["version"]) +PY + ) + if [[ ! "$version" =~ ^[0-9A-Za-z][0-9A-Za-z._-]*$ ]]; then + echo "Error: metadata.json contains an invalid version: $version" + exit 1 + fi local digest_file="$image_dir/digest.txt" local os_image_hash="" if [ -f "$digest_file" ]; then os_image_hash=$(tr -d '\n\r' < "$digest_file") + if [[ ! "$os_image_hash" =~ ^[0-9A-Fa-f]{64}$ ]]; then + echo "Error: digest.txt must contain one SHA-256 hex digest" + exit 1 + fi + os_image_hash=${os_image_hash,,} fi # Detect image variant from directory name @@ -118,7 +139,15 @@ cmd_push() { # Collect all files local files=() for f in "$image_dir"/*; do - [ -f "$f" ] && files+=("$(basename "$f")") + if [ -f "$f" ]; then + local name + name=$(basename "$f") + if [[ ! "$name" =~ ^[0-9A-Za-z][0-9A-Za-z._-]*$ ]]; then + echo "Error: image filename is not OCI-packaging safe: $name" + exit 1 + fi + files+=("$name") + fi done # Generate Dockerfile @@ -161,11 +190,19 @@ cmd_push() { if [ -n "$os_image_hash" ]; then local mr_tag="mr-sha256-${os_image_hash}" local mr_dir - mr_dir=$(mktemp -d) + mr_dir="$tmp_dir/measurement" + mkdir -p "$mr_dir" # Read rootfs filename from metadata to exclude it local rootfs_name - rootfs_name=$(python3 -c "import json; print(json.load(open('$metadata')).get('rootfs', ''))") + rootfs_name=$(python3 - "$metadata" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as file: + print(json.load(file).get("rootfs", "")) +PY + ) # Collect files excluding rootfs local mr_files=() @@ -197,16 +234,18 @@ cmd_push() { echo "Pushing: $mr_ref" docker push "$mr_ref" - rm -rf "$mr_dir" tags+=("$mr_tag") fi + rm -rf "$tmp_dir" + trap - EXIT + echo "" echo "=== Done ===" for tag in "${tags[@]}"; do echo " ${image_ref}:${tag}" done -} +) # --- LIST --- cmd_list() { @@ -215,8 +254,12 @@ cmd_list() { while [ $# -gt 0 ]; do case "$1" in - --filter) filter="$2"; shift 2 ;; - -h|--help) usage ;; + --filter) + [ $# -ge 2 ] || { echo "Error: --filter requires a value"; exit 1; } + filter="$2" + shift 2 + ;; + -h|--help) usage 0 ;; -*) echo "Unknown option: $1"; exit 1 ;; *) if [ -z "$image_ref" ]; then @@ -243,21 +286,24 @@ cmd_list() { curl -sf "https://${registry}/v2/${repo}/tags/list" 2>/dev/null || \ echo '{"tags":[]}') - python3 -c " -import json, sys, re + python3 -c ' +import json +import re +import sys + data = json.load(sys.stdin) -tags = sorted(data.get('Tags', data.get('tags', []))) -filt = '$filter' +tags = sorted(data.get("Tags", data.get("tags", []))) +filt = sys.argv[1] for tag in tags: if not filt or re.search(filt, tag): - print(f' {tag}') -" <<< "$tags_json" + print(f" {tag}") +' "$filter" <<< "$tags_json" } # Dispatch case "$COMMAND" in push) cmd_push "$@" ;; list) cmd_list "$@" ;; - -h|--help) usage ;; + -h|--help) usage 0 ;; *) echo "Unknown command: $COMMAND"; usage ;; esac diff --git a/os/image/mk-image-mr.sh b/os/image/mk-image-mr.sh new file mode 100755 index 000000000..3c69ab7fb --- /dev/null +++ b/os/image/mk-image-mr.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +# Function to display usage +usage() { + local status=${1:-1} + echo "Usage: ${0##*/} " + echo "Example: $0 https://github.com/Dstack-TEE/dstack/releases/download/guest-os-v0.6.0/dstack-0.6.0.tar.gz" + echo "Example: $0 /path/to/local/file.tar.gz" + exit "$status" +} + +# Check if argument is provided +if [ $# -ne 1 ]; then + usage +fi +case "$1" in + -h|--help) + usage 0 + ;; +esac + +INPUT="$1" +TEMP_DIR=$(mktemp -d) +EXTRACT_DIR="$TEMP_DIR/extracted" + +# Cleanup function +cleanup() { + echo "Cleaning up temporary directory: $TEMP_DIR" + rm -rf "$TEMP_DIR" +} +trap cleanup EXIT + +echo "Working directory: $TEMP_DIR" + +# Download or copy the file +if [[ "$INPUT" =~ ^https?:// ]]; then + echo "Downloading from URL: $INPUT" + ARCHIVE_FILE="$TEMP_DIR/archive.tar.gz" + if command -v curl >/dev/null 2>&1; then + curl -fL -o "$ARCHIVE_FILE" "$INPUT" + elif command -v wget >/dev/null 2>&1; then + wget -O "$ARCHIVE_FILE" "$INPUT" + else + echo "Error: Neither curl nor wget is available for downloading" + exit 1 + fi +else + echo "Using local file: $INPUT" + if [ ! -f "$INPUT" ]; then + echo "Error: Local file does not exist: $INPUT" + exit 1 + fi + ARCHIVE_FILE=$(realpath "$INPUT") +fi + +# Create extraction directory +mkdir -p "$EXTRACT_DIR" + +# Extract the archive +echo "Extracting archive to: $EXTRACT_DIR" +tar -xzf "$ARCHIVE_FILE" -C "$EXTRACT_DIR" \ + --no-same-owner --no-same-permissions + +# Find and read the digest +mapfile -d '' -t DIGEST_FILES < <(find "$EXTRACT_DIR" -name digest.txt -type f -print0) +if [ "${#DIGEST_FILES[@]}" -ne 1 ]; then + echo "Error: expected exactly one digest.txt in the archive, found ${#DIGEST_FILES[@]}" + exit 1 +fi +DIGEST_FILE=${DIGEST_FILES[0]} + +DIGEST=$(tr -d '\n\r' < "$DIGEST_FILE" | tr 'A-F' 'a-f') +if [[ ! "$DIGEST" =~ ^[0-9a-f]{64}$ ]]; then + echo "Error: digest.txt must contain exactly one SHA-256 hex digest" + exit 1 +fi + +echo "Found digest: $DIGEST" + +# Remove rootfs file(s) +echo "Removing rootfs files..." +REMOVED_COUNT=$(find "$EXTRACT_DIR" -name "rootfs*" -type f | wc -l) +find "$EXTRACT_DIR" -name "rootfs*" -type f -delete +echo "Removed $REMOVED_COUNT rootfs file(s)" + +# Create flattened structure in a new directory +FLATTEN_DIR="$TEMP_DIR/flattened" +mkdir -p "$FLATTEN_DIR" + +echo "Flattening directory structure..." +# Find all files (not directories) and copy them to the flattened directory. +# Refuse duplicate basenames instead of silently overwriting an artifact. +declare -A SEEN_BASENAMES=() +while IFS= read -r -d '' file; do + name=$(basename "$file") + if [[ -n "${SEEN_BASENAMES[$name]:-}" ]]; then + echo "Error: duplicate archive basename while flattening: $name" >&2 + exit 1 + fi + SEEN_BASENAMES[$name]=1 + cp "$file" "$FLATTEN_DIR/$name" +done < <(find "$EXTRACT_DIR" -type f -print0) + +# Count files for verification +FILE_COUNT=$(find "$FLATTEN_DIR" -type f | wc -l) +echo "Flattened $FILE_COUNT files" + +# Create the final archive with the digest-based name +OUTPUT_FILE="mr_${DIGEST}.tar.gz" +echo "Creating final archive: $OUTPUT_FILE" + +# Change to the flattened directory and create archive without directory structure +cd "$FLATTEN_DIR" +LC_ALL=C tar --sort=name --mtime='@0' --owner=0 --group=0 --numeric-owner \ + -czf "../$OUTPUT_FILE" -- * +cd - >/dev/null + +# Move the final file to the current working directory +mv "$TEMP_DIR/$OUTPUT_FILE" "./$OUTPUT_FILE" + +echo "Successfully created: $OUTPUT_FILE" +echo "Archive contains $FILE_COUNT files with flattened structure" diff --git a/os/spec/artifact-manifest.schema.json b/os/spec/artifact-manifest.schema.json new file mode 100644 index 000000000..feb675142 --- /dev/null +++ b/os/spec/artifact-manifest.schema.json @@ -0,0 +1,102 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "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/Dstack-TEE/dstack/blob/master/os/spec/artifact-manifest.schema.json", + "title": "dstack OS backend artifact manifest", + "description": "Versioned handoff from an OS build backend to the common image assembler. Artifact paths are relative to the manifest.", + "$defs": { + "artifactPath": { + "type": "string", + "minLength": 1, + "pattern": "^(?!/)(?!\\.\\.(?:/|$)).+" + } + }, + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "backend", + "image", + "source", + "boot", + "verity", + "artifacts" + ], + "properties": { + "schema_version": { + "const": 1 + }, + "backend": { + "type": "string", + "minLength": 1 + }, + "image": { + "type": "object", + "additionalProperties": false, + "required": ["name", "version", "flavor", "is_dev"], + "properties": { + "name": {"type": "string", "minLength": 1}, + "version": {"type": "string", "minLength": 1}, + "flavor": {"type": "string", "minLength": 1}, + "is_dev": {"type": "boolean"} + } + }, + "source": { + "type": "object", + "additionalProperties": false, + "required": ["git_revision"], + "properties": { + "git_revision": {"type": "string", "minLength": 1} + } + }, + "boot": { + "type": "object", + "additionalProperties": false, + "required": ["ovmf_variant"], + "properties": { + "ovmf_variant": {"type": "string", "minLength": 1} + } + }, + "verity": { + "type": "object", + "additionalProperties": false, + "required": ["root_hash", "data_size"], + "properties": { + "root_hash": {"type": "string", "minLength": 1}, + "data_size": { + "oneOf": [ + {"type": "integer", "minimum": 1}, + {"type": "string", "pattern": "^[1-9][0-9]*$"} + ] + } + } + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "required": [ + "initramfs", + "kernel", + "firmware", + "rootfs_verity", + "firmware_sev", + "uki" + ], + "properties": { + "initramfs": {"$ref": "#/$defs/artifactPath"}, + "kernel": {"$ref": "#/$defs/artifactPath"}, + "firmware": {"$ref": "#/$defs/artifactPath"}, + "rootfs_verity": {"$ref": "#/$defs/artifactPath"}, + "firmware_sev": { + "oneOf": [{"$ref": "#/$defs/artifactPath"}, {"type": "null"}] + }, + "uki": { + "oneOf": [{"$ref": "#/$defs/artifactPath"}, {"type": "null"}] + } + } + }, + "backend_metadata": { + "type": "object", + "additionalProperties": true + } + } +} diff --git a/os/yocto/.gitignore b/os/yocto/.gitignore new file mode 100644 index 000000000..c6460dc58 --- /dev/null +++ b/os/yocto/.gitignore @@ -0,0 +1,7 @@ +/build +/bb-build +*.qcow2 +__pycache__/ +/.target +/.vscode +/.claude diff --git a/os/yocto/Makefile b/os/yocto/Makefile new file mode 100644 index 000000000..9f0128cf4 --- /dev/null +++ b/os/yocto/Makefile @@ -0,0 +1,46 @@ +ifeq ($(BBPATH),) +$(error BBPATH is not set. Run `source dev-setup` first) +endif + +.PHONY: all dist clean-dstack clean-initrd images images-common images-flavors + +BB_BUILD_DIR ?= bb-build +DIST_DIR ?= ${BB_BUILD_DIR}/dist +export BB_BUILD_DIR +export DIST_DIR + +# Flavor names map to multiconfig names: prod, dev +FLAVORS ?= prod + +# Map flavor to dist name for mkimage.sh +flavor_to_dist = $(if $(filter prod,$1),dstack,$(if $(filter dev,$1),dstack-dev,$1)) + +all: dist + +-include $(wildcard mk.d/*.mk) + +dist: images + $(foreach flavor,$(FLAVORS),./mkimage.sh --dist-name $(call flavor_to_dist,$(flavor)) --flavor $(flavor);) + +# Build common artifacts (shared across all flavors) +# dstack-guest is built here first to warm sstate/downloads and avoid concurrent +# fetch/build conflicts when the per-flavor multiconfigs build it in parallel. +images-common: + bitbake virtual/kernel dstack-initramfs dstack-ovmf dstack-guest + +# Build flavor-specific artifacts using multiconfig (serial to avoid deadlock warnings) +images-flavors: + $(foreach flavor,$(FLAVORS),bitbake mc:$(flavor):dstack-rootfs mc:$(flavor):dstack-uki;) + +images: images-common images-flavors + +clean: + bitbake -c cleansstate virtual/kernel dstack-initramfs dstack-ovmf + $(foreach flavor,$(FLAVORS),bitbake -c cleansstate mc:$(flavor):dstack-rootfs mc:$(flavor):dstack-uki;) + +clean-dstack: + bitbake -c cleansstate dstack-guest + $(foreach flavor,$(FLAVORS),bitbake -c cleansstate mc:$(flavor):dstack-rootfs;) + +clean-initrd: + bitbake -c cleansstate dstack-initramfs diff --git a/os/yocto/README.md b/os/yocto/README.md new file mode 100644 index 000000000..a17a622e4 --- /dev/null +++ b/os/yocto/README.md @@ -0,0 +1,40 @@ +# Yocto backend for the dstack guest OS + +This directory contains the Yocto backend imported from `meta-dstack`. +dstack-owned layers live in `layers/`; external layers and BitBake live in +`deps/` as git submodules. + +For the repository-wide OS layout and backend contract, see [`../README.md`](../README.md). + +## Reproducible build + +From the repository root, the tested one-build entrypoint is: + +```bash +make os-image +``` + +Use `make os-repro-check` to build twice and compare release outputs. The full +prerequisite, output, verification, flavor, incremental-build, and +troubleshooting instructions are in the +[guest-OS build guide](../../docs/building-guest-os.md). + +For an interactive native build, source `dev-setup` and use this directory's +Makefile, or run the repository entrypoint `./os/build.sh --backend yocto`. + +The supported backend script only builds guest images. The backend-specific +`tools/` directory contains only host workarounds needed while building Yocto. +Historical all-in-one host-stack and direct-QEMU helpers are isolated under +[`../../tools/`](../../tools/); they are not added to `PATH` and do not shadow +the supported Rust `dstack` CLI. + +## Output boundary + +`scripts/export-artifacts.sh` converts BitBake deployment paths to the common +artifact manifest. `mkimage.sh` is a compatibility wrapper that exports the +manifest and calls `../image/assemble.sh`. + +## License + +The dstack-owned backend code is Apache-2.0. Embedded and third-party +components retain their own license declarations and notices. diff --git a/os/yocto/build.sh b/os/yocto/build.sh new file mode 100755 index 000000000..4f8d8baee --- /dev/null +++ b/os/yocto/build.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Yocto implementation of the guest-OS backend contract in os/README.md. +set -eo pipefail + +usage() { + cat <<'USAGE' +Usage: build.sh image [build-dir] + +Build dstack guest-OS artifacts with Yocto and assemble release archives. +The guest action is accepted as a compatibility alias for image. + +Environment: + FLAVORS space-separated image flavors (default: prod) + DIST_DIR release output directory (default: /images) +USAGE +} + +ACTION=${1:-} +BUILD_DIR=${2:-} +if [ "$#" -gt 2 ]; then + usage >&2 + exit 1 +fi + +case "$ACTION" in + image|guest) + ;; + help|-h|--help) + usage + exit 0 + ;; + *) + if [ -n "$ACTION" ]; then + echo "Invalid action: $ACTION" >&2 + fi + usage >&2 + exit 1 + ;; +esac + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +DIST_DIR=${DIST_DIR:-"$PWD/images"} +FLAVORS=${FLAVORS:-prod} +read -r -a FLAVOR_LIST <<< "$FLAVORS" +if [ "${#FLAVOR_LIST[@]}" -eq 0 ]; then + echo "Error: at least one Yocto image flavor is required" >&2 + exit 1 +fi +for flavor in "${FLAVOR_LIST[@]}"; do + case "$flavor" in + prod|dev) + ;; + *) + echo "Error: unsupported Yocto image flavor: $flavor" >&2 + exit 1 + ;; + esac +done + +if [ -z "${BBPATH:-}" ]; then + # Always pass a directory explicitly. A sourced script otherwise inherits + # this script's positional parameters and would mistake `image` for the + # build directory. + BUILD_DIR=${BUILD_DIR:-"$SCRIPT_DIR/bb-build"} + # shellcheck disable=SC1091 + source "$SCRIPT_DIR/dev-setup" "$BUILD_DIR" +elif [ -n "$BUILD_DIR" ] && [ "$(realpath "$BUILD_DIR")" != "$(realpath "$BBPATH")" ]; then + echo "Error: BBPATH is already set to $BBPATH, not requested build directory $BUILD_DIR" >&2 + exit 1 +fi + +cat <<'NOTE' +Note: the first docker-compose fetch contains hundreds of separately +checksummed Go modules. BitBake's 0-100% display resets for each module; this +is forward progress, not the same archive being downloaded repeatedly. +Interrupted downloads are retained and reused on the next run. +NOTE + +make -C "$SCRIPT_DIR" dist \ + DIST_DIR="$DIST_DIR" \ + BB_BUILD_DIR="$BBPATH" \ + FLAVORS="$FLAVORS" diff --git a/os/yocto/deps/bitbake b/os/yocto/deps/bitbake new file mode 160000 index 000000000..22021758e --- /dev/null +++ b/os/yocto/deps/bitbake @@ -0,0 +1 @@ +Subproject commit 22021758e66737bcf68dfd2b74adc6a0cb1d42d9 diff --git a/os/yocto/deps/meta-confidential-compute b/os/yocto/deps/meta-confidential-compute new file mode 160000 index 000000000..c829fbc40 --- /dev/null +++ b/os/yocto/deps/meta-confidential-compute @@ -0,0 +1 @@ +Subproject commit c829fbc4032490ceb900a2ed38f066a515aa0bb3 diff --git a/os/yocto/deps/meta-openembedded b/os/yocto/deps/meta-openembedded new file mode 160000 index 000000000..9af4488d4 --- /dev/null +++ b/os/yocto/deps/meta-openembedded @@ -0,0 +1 @@ +Subproject commit 9af4488d46cb4fd4c0d2d64820c86225ebd6ac71 diff --git a/os/yocto/deps/meta-rust-bin b/os/yocto/deps/meta-rust-bin new file mode 160000 index 000000000..e094f7834 --- /dev/null +++ b/os/yocto/deps/meta-rust-bin @@ -0,0 +1 @@ +Subproject commit e094f7834f786aa65e76209e528a1d7eee08a5ea diff --git a/os/yocto/deps/meta-security b/os/yocto/deps/meta-security new file mode 160000 index 000000000..89aec22c1 --- /dev/null +++ b/os/yocto/deps/meta-security @@ -0,0 +1 @@ +Subproject commit 89aec22c16c247e3b0e11fa07750762102a51abc diff --git a/os/yocto/deps/meta-virtualization b/os/yocto/deps/meta-virtualization new file mode 160000 index 000000000..eee9e184c --- /dev/null +++ b/os/yocto/deps/meta-virtualization @@ -0,0 +1 @@ +Subproject commit eee9e184c3d92869cc184afd119e7b39dc1fd63d diff --git a/os/yocto/deps/meta-yocto b/os/yocto/deps/meta-yocto new file mode 160000 index 000000000..8251bdad5 --- /dev/null +++ b/os/yocto/deps/meta-yocto @@ -0,0 +1 @@ +Subproject commit 8251bdad5fda780a000fb41e6eda82eadf0fa39e diff --git a/os/yocto/deps/openembedded-core b/os/yocto/deps/openembedded-core new file mode 160000 index 000000000..06dd66e62 --- /dev/null +++ b/os/yocto/deps/openembedded-core @@ -0,0 +1 @@ +Subproject commit 06dd66e6220e5ce4ed4b9af4d8231ae5f0a8ce80 diff --git a/os/yocto/dev-setup b/os/yocto/dev-setup new file mode 100755 index 000000000..31032a39e --- /dev/null +++ b/os/yocto/dev-setup @@ -0,0 +1,61 @@ +#!/bin/bash +shopt -s nullglob + +if [ -n "${BASH_SOURCE[0]:-}" ]; then + THIS_SCRIPT=${BASH_SOURCE[0]} +elif [ -n "${ZSH_NAME:-}" ]; then + THIS_SCRIPT=$0 +else + THIS_SCRIPT="$(pwd)/dev-setup" + if [ ! -e "$THIS_SCRIPT" ]; then + echo "Error: $THIS_SCRIPT doesn't exist!" >&2 + echo "Please run this script in dev-setup's directory." >&2 + exit 1 + fi +fi +THIS_SCRIPT=$(realpath "$THIS_SCRIPT") +THIS_DIR=$(dirname "$THIS_SCRIPT") + +LAYERS="$THIS_DIR/deps/meta-yocto/meta-poky \ + $THIS_DIR/deps/meta-yocto/meta-yocto-bsp \ + $THIS_DIR/deps/meta-confidential-compute \ + $THIS_DIR/deps/meta-openembedded/meta-oe \ + $THIS_DIR/deps/meta-openembedded/meta-python \ + $THIS_DIR/deps/meta-openembedded/meta-networking \ + $THIS_DIR/deps/meta-openembedded/meta-filesystems \ + $THIS_DIR/deps/meta-virtualization \ + $THIS_DIR/deps/meta-rust-bin \ + $THIS_DIR/deps/meta-security \ + $THIS_DIR/deps/meta-security/meta-tpm \ + $THIS_DIR/layers/meta-dstack" + +if [ -z "${1:-}" ]; then + BUILD_DIR=$THIS_DIR/bb-build +else + BUILD_DIR=$(realpath -m "$1") +fi + +# Sync build config from the dstack layer (always overwrite) +CONF_SRC=$THIS_DIR/layers/meta-dstack/conf +mkdir -p "$BUILD_DIR/conf" +cp -f "$CONF_SRC/local.conf" "$BUILD_DIR/conf/local.conf" +rm -rf "$BUILD_DIR/conf/multiconfig" +cp -a "$CONF_SRC/multiconfig" "$BUILD_DIR/conf/" + +OE_INIT=$THIS_DIR/deps/openembedded-core/oe-init-build-env + +for script in "$THIS_DIR"/setup.d/*.sh; do + # shellcheck disable=SC1090 + source "$script" +done + +# shellcheck disable=SC2317 +pushd "$BUILD_DIR" >/dev/null || { return 1 2>/dev/null || exit 1; } +# shellcheck disable=SC1090 +BDIR="." TEMPLATECONF=$THIS_DIR/deps/openembedded-core/meta/conf/templates/default source "$OE_INIT" +# shellcheck disable=SC2317 +popd >/dev/null || { return 1 2>/dev/null || exit 1; } + +# LAYERS is deliberately a space-separated list populated by setup.d hooks. +# shellcheck disable=SC2086 +bitbake-layers add-layer $LAYERS diff --git a/os/yocto/layers/meta-dstack/conf/distro/dstack.conf b/os/yocto/layers/meta-dstack/conf/distro/dstack.conf new file mode 100644 index 000000000..137441eb7 --- /dev/null +++ b/os/yocto/layers/meta-dstack/conf/distro/dstack.conf @@ -0,0 +1,45 @@ +# Select systemd init before requiring poky-derived distro config, otherwise +# poky.conf's POKY_INIT_MANAGER="sysvinit" pulls init-manager-sysvinit.inc which +# appends sysvinit to DISTRO_FEATURES and conflicts with systemd (breaks udev). +INIT_MANAGER = "systemd" +require conf/distro/cvm.conf +DISTRO = "dstack" +DISTRO_NAME = "DStack" +DISTRO_FEATURES:append = " virtualization seccomp systemd usrmerge security dm-verity ipv6" + +DISTRO_VERSION = "0.6.0" +DISTROOVERRIDES = "poky:dstack" +INITRAMFS_IMAGE = "" + +PREFERRED_PROVIDER_virtual/kernel = "linux-yocto" +PREFERRED_VERSION_linux-yocto ?= "6.18%" +LINUX_KERNEL_TYPE = "tiny" + +MACHINE_FEATURES += "numa" + +VIRTUAL-RUNTIME_init_manager = "systemd" +VIRTUAL-RUNTIME_initscripts = "" +VIRTUAL-RUNTIME_dev_manager = "systemd" + +SERIAL_CONSOLES = "115200;ttyS0" +PREFERRED_VERSION_rust-bin-cross-x86_64 = "1.92.0" +PREFERRED_VERSION_cargo-bin-cross-x86_64 = "1.92.0" + +# NVIDIA driver stack (included in the default images). +# Bump all three together — kernel module ABI is paired with userspace libs. +NVIDIA_VERSION = "595.58.03" +PREFERRED_VERSION_nvidia = "${NVIDIA_VERSION}" +PREFERRED_VERSION_nvidia-fabricmanager = "${NVIDIA_VERSION}" +PREFERRED_VERSION_libnvidia-nscq = "${NVIDIA_VERSION}" + +BAD_RECOMMENDATIONS = "busybox-syslog systemd-compat-units" + +# Skip unused components that fail metadata checks +SKIP_RECIPE[ostree] = "not required for dstack" + +# Suppress meta-tpm warning (we use TPM drivers directly without tpm2 DISTRO_FEATURES) +SKIP_META_TPM_SANITY_CHECK = "1" + +# EFI/UKI support for GCP images +MACHINE_FEATURES:append = " efi" +EFI_PROVIDER = "systemd-boot" diff --git a/os/yocto/layers/meta-dstack/conf/layer.conf b/os/yocto/layers/meta-dstack/conf/layer.conf new file mode 100644 index 000000000..10bb0a01c --- /dev/null +++ b/os/yocto/layers/meta-dstack/conf/layer.conf @@ -0,0 +1,8 @@ +BBPATH .= ":${LAYERDIR}" +BBFILES += "${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend" + +BBFILE_COLLECTIONS += "dstack" +BBFILE_PATTERN_dstack := "^${LAYERDIR}/" +BBFILE_PRIORITY_dstack = "20" +LAYERVERSION_dstack = "4" +LAYERSERIES_COMPAT_dstack = "wrynose" diff --git a/os/yocto/layers/meta-dstack/conf/local.conf b/os/yocto/layers/meta-dstack/conf/local.conf new file mode 100644 index 000000000..cc65caedc --- /dev/null +++ b/os/yocto/layers/meta-dstack/conf/local.conf @@ -0,0 +1,299 @@ +# +# This file is your local configuration file and is where all local user settings +# are placed. The comments in this file give some guide to the options a new user +# to the system might want to change but pretty much any configuration option can +# be set in this file. More adventurous users can look at +# local.conf.sample.extended which contains other examples of configuration which +# can be placed in this file but new users likely won't need any of them +# initially. There's also site.conf.sample which contains examples of site specific +# information such as proxy server addresses. +# +# Lines starting with the '#' character are commented out and in some cases the +# default values are provided as comments to show people example syntax. Enabling +# the option is a question of removing the # character and making any change to the +# variable as required. + +# +# Machine Selection +# +# You need to select a specific machine to target the build with. There are a selection +# of emulated machines available which can boot and run in the QEMU emulator: +# +#MACHINE ?= "qemuarm" +#MACHINE ?= "qemuarm64" +#MACHINE ?= "qemumips" +#MACHINE ?= "qemumips64" +#MACHINE ?= "qemuppc" +#MACHINE ?= "qemux86" +#MACHINE ?= "qemux86-64" +# +# There are also the following hardware board target machines included for +# demonstration purposes: +# +#MACHINE ?= "beaglebone-yocto" +#MACHINE ?= "genericarm64" +#MACHINE ?= "genericx86" +#MACHINE ?= "genericx86-64" +# +# This sets the default machine to be qemux86-64 if no other machine is selected: +MACHINE ??= "dstack" + +# These are some of the more commonly used values. Looking at the files in the +# meta/conf/machine directory, or the conf/machine directory of any additional layers +# you add in will show all the available machines. + +# +# Where to place downloads +# +# During a first build the system will download many different source code tarballs +# from various upstream projects. This can take a while, particularly if your network +# connection is slow. These are all stored in DL_DIR. When wiping and rebuilding you +# can preserve this directory to speed up this part of subsequent builds. This directory +# is safe to share between multiple builds on the same machine too. +# +# The default is a downloads directory under TOPDIR which is the build directory. +# +#DL_DIR ?= "${TOPDIR}/downloads" + +# +# Where to place shared-state files +# +# BitBake has the capability to accelerate builds based on previously built output. +# This is done using "shared state" files which can be thought of as cache objects +# and this option determines where those files are placed. +# +# You can wipe out TMPDIR leaving this directory intact and the build would regenerate +# from these files if no changes were made to the configuration. If changes were made +# to the configuration, only shared state files where the state was still valid would +# be used (done using checksums). +# +# The default is a sstate-cache directory under TOPDIR. +# +#SSTATE_DIR ?= "${TOPDIR}/sstate-cache" + +# +# Where to place the build output +# +# This option specifies where the bulk of the building work should be done and +# where BitBake should place its temporary files and output. Keep in mind that +# this includes the extraction and compilation of many applications and the toolchain +# which can use Gigabytes of hard disk space. +# +# The default is a tmp directory under TOPDIR. +# +#TMPDIR = "${TOPDIR}/tmp" + +# +# Default policy config +# +# The distribution setting controls which policy settings are used as defaults. +# The default value is fine for general Yocto project use, at least initially. +# Ultimately when creating custom policy, people will likely end up subclassing +# these defaults. +# +DISTRO ?= "dstack" +# As an example of a subclass there is a "bleeding" edge policy configuration +# where many versions are set to the absolute latest code from the upstream +# source control systems. This is just mentioned here as an example, its not +# useful to most new users. +# DISTRO ?= "poky-bleeding" + +# +# Package Management configuration +# +# This variable lists which packaging formats to enable. Multiple package backends +# can be enabled at once and the first item listed in the variable will be used +# to generate the root filesystems. +# Options are: +# - 'package_deb' for debian style deb files +# - 'package_ipk' for ipk files are used by opkg (a debian style embedded package manager) +# - 'package_rpm' for rpm style packages +# E.g.: PACKAGE_CLASSES ?= "package_rpm package_deb package_ipk" +# OE-Core defaults to ipkg, whilst Poky defaults to rpm: +# PACKAGE_CLASSES ?= "package_rpm" + +# +# SDK target architecture +# +# This variable specifies the architecture to build SDK items for and means +# you can build the SDK packages for architectures other than the machine you are +# running the build on (i.e. building i686 packages on an x86_64 host). +# Supported values are i686, x86_64, aarch64 +#SDKMACHINE ?= "i686" + +# +# Extra image configuration defaults +# +# The EXTRA_IMAGE_FEATURES variable allows extra packages to be added to the generated +# images. Some of these options are added to certain image types automatically. The +# variable can contain the following options: +# "dbg-pkgs" - add -dbg packages for all installed packages +# (adds symbol information for debugging/profiling) +# "src-pkgs" - add -src packages for all installed packages +# (adds source code for debugging) +# "dev-pkgs" - add -dev packages for all installed packages +# (useful if you want to develop against libs in the image) +# "ptest-pkgs" - add -ptest packages for all ptest-enabled packages +# (useful if you want to run the package test suites) +# "tools-sdk" - add development tools (gcc, make, pkgconfig etc.) +# "tools-debug" - add debugging tools (gdb, strace) +# "eclipse-debug" - add Eclipse remote debugging support +# "tools-profile" - add profiling tools (oprofile, lttng, valgrind) +# "tools-testapps" - add useful testing tools (ts_print, aplay, arecord etc.) +# "debug-tweaks" - make an image suitable for development +# e.g. ssh root access has a blank password +# There are other application targets that can be used here too, see +# meta/classes-recipe/image.bbclass and +# meta/classes-recipe/core-image.bbclass for more details. +# We default to enabling the debugging tweaks. +# EXTRA_IMAGE_FEATURES ?= "debug-tweaks" + +# +# Additional image features +# +# The following is a list of additional classes to use when building images which +# enable extra features. Some available options which can be included in this variable +# are: +# - 'buildstats' collect build statistics +USER_CLASSES ?= "buildstats" + +# Prefer faster GNU mirror +GNU_MIRRORS = "https://ftpmirror.gnu.org/gnu/" + +# +# Runtime testing of images +# +# The build system can test booting virtual machine images under qemu (an emulator) +# after any root filesystems are created and run tests against those images. It can also +# run tests against any SDK that are built. To enable this uncomment these lines. +# See meta/classes-recipe/test{image,sdk}.bbclass for further details. +#IMAGE_CLASSES += "testimage testsdk" +#TESTIMAGE_AUTO:qemuall = "1" + +# +# Interactive shell configuration +# +# Under certain circumstances the system may need input from you and to do this it +# can launch an interactive shell. It needs to do this since the build is +# multithreaded and needs to be able to handle the case where more than one parallel +# process may require the user's attention. The default is iterate over the available +# terminal types to find one that works. +# +# Examples of the occasions this may happen are when resolving patches which cannot +# be applied, to use the devshell or the kernel menuconfig +# +# Supported values are auto, gnome, xfce, rxvt, screen, konsole (KDE 3.x only), none +# Note: currently, Konsole support only works for KDE 3.x due to the way +# newer Konsole versions behave +#OE_TERMINAL = "auto" +# By default disable interactive patch resolution (tasks will just fail instead): +PATCHRESOLVE = "noop" + +# +# Disk Space Monitoring during the build +# +# Monitor the disk space during the build. If there is less that 1GB of space or less +# than 100K inodes in any key build location (TMPDIR, DL_DIR, SSTATE_DIR), gracefully +# shutdown the build. If there is less than 100MB or 1K inodes, perform a hard halt +# of the build. The reason for this is that running completely out of space can corrupt +# files and damages the build in ways which may not be easily recoverable. +# It's necessary to monitor /tmp, if there is no space left the build will fail +# with very exotic errors. +BB_DISKMON_DIRS ??= "\ + STOPTASKS,${TMPDIR},1G,100K \ + STOPTASKS,${DL_DIR},1G,100K \ + STOPTASKS,${SSTATE_DIR},1G,100K \ + STOPTASKS,/tmp,100M,100K \ + HALT,${TMPDIR},100M,1K \ + HALT,${DL_DIR},100M,1K \ + HALT,${SSTATE_DIR},100M,1K \ + HALT,/tmp,10M,1K" + +# +# Shared-state files from other locations +# +# As mentioned above, shared state files are prebuilt cache data objects which can be +# used to accelerate build time. This variable can be used to configure the system +# to search other mirror locations for these objects before it builds the data itself. +# +# This can be a filesystem directory, or a remote url such as https or ftp. These +# would contain the sstate-cache results from previous builds (possibly from other +# machines). This variable works like fetcher MIRRORS/PREMIRRORS and points to the +# cache locations to check for the shared objects. +# NOTE: if the mirror uses the same structure as SSTATE_DIR, you need to add PATH +# at the end as shown in the examples below. This will be substituted with the +# correct path within the directory structure. +#SSTATE_MIRRORS ?= "\ +#file://.* https://someserver.tld/share/sstate/PATH;downloadfilename=PATH \ +#file://.* file:///some/local/dir/sstate/PATH" + +# +# Yocto Project SState Mirror +# +# The Yocto Project has prebuilt artefacts available for its releases, you can enable +# use of these by uncommenting some of the following lines. This will mean the build uses +# the network to check for artefacts at the start of builds, which does slow it down +# initially but it will then speed up the builds by not having to build things if they are +# present in the cache. It assumes you can download something faster than you can build it +# which will depend on your network. +# Note: For this to work you also need hash-equivalence passthrough to the matching server +# There is a choice between our sstate server directly and a faster content delivery network +# (CDN) kindly provided by JSDelivr, uncomment one of the SSTATE_MIRRORS lines, not both. +# Using the CDN rather than the yoctoproject.org address is suggested/preferred. +# +#BB_HASHSERVE_UPSTREAM = 'wss://hashserv.yoctoproject.org/ws' +#SSTATE_MIRRORS ?= "file://.* https://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH" +# +###SSTATE_MIRRORS ?= "file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH" + + +# +# Qemu configuration +# +# By default native qemu will build with a builtin VNC server where graphical output can be +# seen. The line below enables the SDL UI frontend too. +# PACKAGECONFIG:append:pn-qemu-system-native = " sdl" +# By default libsdl2-native will be built, if you want to use your host's libSDL instead of +# the minimal libsdl built by libsdl2-native then uncomment the ASSUME_PROVIDED line below. +#ASSUME_PROVIDED += "libsdl2-native" + +# You can also enable the Gtk UI frontend, which takes somewhat longer to build, but adds +# a handy set of menus for controlling the emulator. +#PACKAGECONFIG:append:pn-qemu-system-native = " gtk+" + +# +# Hash Equivalence +# +# Enable support for automatically running a local hash equivalence server and +# instruct bitbake to use a hash equivalence aware signature generator. Hash +# equivalence improves reuse of sstate by detecting when a given sstate +# artifact can be reused as equivalent, even if the current task hash doesn't +# match the one that generated the artifact. +# +# A shared hash equivalent server can be set with ":" format +# +#BB_HASHSERVE = "auto" +#BB_SIGNATURE_HANDLER = "OEEquivHash" + +# +# Memory Resident Bitbake +# +# Bitbake's server component can stay in memory after the UI for the current command +# has completed. This means subsequent commands can run faster since there is no need +# for bitbake to reload cache files and so on. Number is in seconds, after which the +# server will shut down. +# +#BB_SERVER_TIMEOUT = "60" + +# CONF_VERSION is increased each time build/conf/ changes incompatibly and is used to +# track the version of this file when it was generated. This can safely be ignored if +# this doesn't mean anything to you. +CONF_VERSION = "2" + +# Prefer kernel.org yocto source mirror to avoid slow downloads.yoctoproject.org paths +INHERIT += "own-mirrors" +SOURCE_MIRROR_URL = "https://mirrors.kernel.org/yocto-sources/" + +# Improve resilience for large files on unstable links +FETCHCMD_wget = "wget --progress=dot --inet4-only -c" +BBMULTICONFIG = "prod dev" diff --git a/os/yocto/layers/meta-dstack/conf/machine/dstack.conf b/os/yocto/layers/meta-dstack/conf/machine/dstack.conf new file mode 100644 index 000000000..3770f997b --- /dev/null +++ b/os/yocto/layers/meta-dstack/conf/machine/dstack.conf @@ -0,0 +1,53 @@ +#@TYPE: Machine +#@NAME: dstack confidential guest +#@DESCRIPTION: Unified dstack confidential-guest machine. A single image that +# boots on both Intel TDX and AMD SEV-SNP hosts; the kernel +# detects the platform at runtime. Kernel feature fragments +# (tdx.scc / sev-snp.scc / ...) are reused from +# meta-confidential-compute via KERNEL_FEATURES in the +# linux-yocto bbappend. +# +# The QEMU/tune boilerplate below mirrors the generic x86-64 confidential-guest +# machines in meta-confidential-compute (tdx.conf / sev-snp.conf). It is kept +# self-contained here so the dstack machine does not depend on that layer's +# machine names. + +# from require conf/machine/include/qemu.inc +# Don't include kernels in standard images +RDEPENDS:${KERNEL_PACKAGE_NAME}-base = "" + +# Use a common kernel recipe for all QEMU machines +PREFERRED_PROVIDER_virtual/kernel ??= "linux-yocto-tiny" + +EXTRA_IMAGEDEPENDS += "qemu-system-native qemu-helper-native:do_addto_recipe_sysroot" + +# from require conf/machine/include/x86/qemuboot-x86.inc +# For runqemu +IMAGE_CLASSES += "qemuboot" +QB_SMP ?= "-smp 4" +QB_CPU:x86 ?= "-cpu IvyBridge -machine q35,i8042=off" +QB_CPU_KVM:x86 ?= "-cpu IvyBridge -machine q35,i8042=off" + +QB_CPU:x86-64 ?= "-cpu IvyBridge -machine q35,i8042=off" +QB_CPU_KVM:x86-64 ?= "-cpu IvyBridge -machine q35,i8042=off" + +# No QB_KERNEL_CMDLINE_APPEND: that only affects `runqemu` test boots, not the +# production image cmdline (built in mkimage.sh). The tdx machine inherited one +# that disabled the Spectre v2 mitigation (nospectre_v2); dropping it keeps test +# boots at the kernel's default mitigations, matching production. +QB_OPT_APPEND = "-usb -device usb-tablet -usb -device usb-kbd" + +DEFAULTTUNE ?= "x86-64-v3" + +require conf/machine/include/x86/tune-x86-64-v3.inc + +KERNEL_IMAGETYPE = "bzImage" + +SERIAL_CONSOLES ?= "115200;ttyS0 115200;ttyS1" + +MACHINE_FEATURES += "x86 pci tpm2" + +do_image_wic[depends] += "syslinux:do_populate_sysroot syslinux-native:do_populate_sysroot mtools-native:do_populate_sysroot dosfstools-native:do_populate_sysroot" + +# For runqemu +QB_SYSTEM_NAME = "dstack" diff --git a/os/yocto/layers/meta-dstack/conf/multiconfig/dev.conf b/os/yocto/layers/meta-dstack/conf/multiconfig/dev.conf new file mode 100644 index 000000000..8c1a766d3 --- /dev/null +++ b/os/yocto/layers/meta-dstack/conf/multiconfig/dev.conf @@ -0,0 +1,6 @@ +# Development flavor configuration +DSTACK_FLAVOR = "dev" +DSTACK_DEV = "1" + +# Use separate TMPDIR to avoid conflicts between multiconfigs +TMPDIR = "${TOPDIR}/tmp-mc-dev" diff --git a/os/yocto/layers/meta-dstack/conf/multiconfig/prod.conf b/os/yocto/layers/meta-dstack/conf/multiconfig/prod.conf new file mode 100644 index 000000000..586ceb191 --- /dev/null +++ b/os/yocto/layers/meta-dstack/conf/multiconfig/prod.conf @@ -0,0 +1,6 @@ +# Production flavor configuration +DSTACK_FLAVOR = "prod" +DSTACK_DEV = "0" + +# Use separate TMPDIR to avoid conflicts between multiconfigs +TMPDIR = "${TOPDIR}/tmp-mc-prod" diff --git a/os/yocto/layers/meta-dstack/recipes-connectivity/openssh/files/disable-password-auth.conf b/os/yocto/layers/meta-dstack/recipes-connectivity/openssh/files/disable-password-auth.conf new file mode 100644 index 000000000..47963f278 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-connectivity/openssh/files/disable-password-auth.conf @@ -0,0 +1,8 @@ +# Disable all password-based authentication +PasswordAuthentication no +PermitEmptyPasswords no +KbdInteractiveAuthentication no + +# Only allow public key authentication +PubkeyAuthentication yes +PermitRootLogin prohibit-password diff --git a/os/yocto/layers/meta-dstack/recipes-connectivity/openssh/openssh_%.bbappend b/os/yocto/layers/meta-dstack/recipes-connectivity/openssh/openssh_%.bbappend new file mode 100644 index 000000000..81a2e54ab --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-connectivity/openssh/openssh_%.bbappend @@ -0,0 +1,10 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SRC_URI += "file://disable-password-auth.conf" + +do_install:append() { + install -d ${D}${sysconfdir}/ssh/sshd_config.d + install -m 0644 ${UNPACKDIR}/disable-password-auth.conf ${D}${sysconfdir}/ssh/sshd_config.d/ +} + +FILES:${PN}-sshd += "${sysconfdir}/ssh/sshd_config.d/" diff --git a/os/yocto/layers/meta-dstack/recipes-core/base-files/base-files%.bbappend b/os/yocto/layers/meta-dstack/recipes-core/base-files/base-files%.bbappend new file mode 100644 index 000000000..ed7056d5d --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/base-files/base-files%.bbappend @@ -0,0 +1,13 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SRC_URI += "file://dstack-motd" + +do_install:append() { + if [ -f ${UNPACKDIR}/dstack-motd ];then + bbnote "Installing custom dstack motd file" + install -m 0644 ${UNPACKDIR}/dstack-motd ${D}${sysconfdir}/motd + else + bbwarn "Custom dstack-motd file not found in ${UNPACKDIR}" + ls -la ${UNPACKDIR} + fi +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/base-files/files/dstack-motd b/os/yocto/layers/meta-dstack/recipes-core/base-files/files/dstack-motd new file mode 100644 index 000000000..5376caf7e --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/base-files/files/dstack-motd @@ -0,0 +1,8 @@ +██████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗ ████████╗███████╗███████╗ ██████╗ ███████╗ +██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ ╚══██╔══╝██╔════╝██╔════╝ ██╔═══██╗██╔════╝ +██║ ██║███████╗ ██║ ███████║██║ █████╔╝ ██║ █████╗ █████╗ ██║ ██║███████╗ +██║ ██║╚════██║ ██║ ██╔══██║██║ ██╔═██╗ ██║ ██╔══╝ ██╔══╝ ██║ ██║╚════██║ +██████╔╝███████║ ██║ ██║ ██║╚██████╗██║ ██╗ ██║ ███████╗███████╗ ╚██████╔╝███████║ +╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝ ╚═════╝ ╚══════╝ + +Welcome to dstack! diff --git a/os/yocto/layers/meta-dstack/recipes-core/busybox/busybox%.bbappend b/os/yocto/layers/meta-dstack/recipes-core/busybox/busybox%.bbappend new file mode 100644 index 000000000..0dd2f3573 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/busybox/busybox%.bbappend @@ -0,0 +1,3 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" +SRC_URI += "file://fragment.cfg" +RRECOMMENDS:${PN} = "" diff --git a/os/yocto/layers/meta-dstack/recipes-core/busybox/files/fragment.cfg b/os/yocto/layers/meta-dstack/recipes-core/busybox/files/fragment.cfg new file mode 100644 index 000000000..34eb67ab3 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/busybox/files/fragment.cfg @@ -0,0 +1,2 @@ +CONFIG_UDHCPD=n +CONFIG_UDHCPC=n diff --git a/os/yocto/layers/meta-dstack/recipes-core/chrony/chrony%.bbappend b/os/yocto/layers/meta-dstack/recipes-core/chrony/chrony%.bbappend new file mode 100644 index 000000000..08e99c90c --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/chrony/chrony%.bbappend @@ -0,0 +1,4 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +DEPENDS += "gnutls" +PACKAGECONFIG:append = " nts" diff --git a/os/yocto/layers/meta-dstack/recipes-core/chrony/files/chrony.conf b/os/yocto/layers/meta-dstack/recipes-core/chrony/files/chrony.conf new file mode 100644 index 000000000..d0d751bf4 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/chrony/files/chrony.conf @@ -0,0 +1,61 @@ +# Load config files matching the /etc/chrony/conf.d/*.conf pattern. +confdir /etc/chrony/conf.d + +# Use public NTP servers from the pool.ntp.org project. +# Please consider joining the pool project if possible by running your own +# server(s). +# If you are a vendor distributing a product using chrony, you *MUST* +# read and comply with http://www.pool.ntp.org/vendors.html +# pool 0.openembedded.pool.ntp.org iburst + +# Use a local timeserver in preference to the pool, if it's reachable. +#server 192.168.22.22 iburst minpoll 2 prefer +server time.cloudflare.com iburst nts +server ptbtime1.ptb.de iburst nts +server ptbtime2.ptb.de iburst nts +server ptbtime3.ptb.de iburst nts +server nts.netnod.se iburst nts +server nts.ntp.se iburst nts +server ntppool1.time.nl iburst nts +server virginia.time.system76.com iburst nts +server oregon.time.system76.com iburst nts +server paris.time.system76.com iburst nts + +# Sync to pulse-per-second from an onboard GPS. +#refclock PPS /dev/pps0 poll 0 prefer +# You'll want to enable CONFIG_PPS and CONFIG_PPS_CLIENT_GPIO in your kernel, +# and an entry something like this in your device tree: +# pps { +# compatible = "pps-gpio"; +# gpios = <&ps7_gpio_0 56 0>; +# }; + +# Load source files matching the /etc/chrony/sources.d/*.sources pattern. +# These can be reloaded using 'chronyc reload sources'. +sourcedir /etc/chrony/sources.d + +# In first three updates step the system clock instead of slew +# if the adjustment is larger than 1 second. +makestep 1.0 3 + +# Record the rate at which the system clock gains/loses time, +# improving accuracy after reboot +driftfile /var/lib/chrony/drift + +# Enable kernel synchronization of the hardware real-time clock (RTC). +# rtcsync + +# Allow NTP client access from local network. +#allow 192.168/16 + +# Serve time even if not synchronized to any NTP server. +#local stratum 10 + +# Specify file containing keys for NTP authentication. +#keyfile /etc/chrony.keys + +# Specify directory for log files. +logdir /var/log/chrony + +# Select which information is logged. +#log measurements statistics tracking diff --git a/os/yocto/layers/meta-dstack/recipes-core/docker/docker-moby%.bbappend b/os/yocto/layers/meta-dstack/recipes-core/docker/docker-moby%.bbappend new file mode 100644 index 000000000..8a6e2d028 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/docker/docker-moby%.bbappend @@ -0,0 +1,13 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SYSTEMD_SERVICE:${PN}:append = " docker.service" + +SRC_URI += "file://docker.service.d_override.conf" +FILES:${PN} += "${systemd_system_unitdir}/docker.service.d/override.conf" + +do_install:append() { + if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then + install -d ${D}${systemd_system_unitdir}/docker.service.d + install -m 0644 "${UNPACKDIR}/docker.service.d_override.conf" ${D}${systemd_system_unitdir}/docker.service.d/override.conf + fi +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/docker/files/docker.service.d_override.conf b/os/yocto/layers/meta-dstack/recipes-core/docker/files/docker.service.d_override.conf new file mode 100644 index 000000000..fecb5127d --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/docker/files/docker.service.d_override.conf @@ -0,0 +1,3 @@ +[Service] +# Pin Docker daemon to CPU 0 for performance isolation +CPUAffinity=0 diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-guest/dstack-guest.bb b/os/yocto/layers/meta-dstack/recipes-core/dstack-guest/dstack-guest.bb new file mode 100644 index 000000000..512c49d1f --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-guest/dstack-guest.bb @@ -0,0 +1,104 @@ +SUMMARY = "Guest binaries for dstack, a decentralized computing stack" +DESCRIPTION = "${SUMMARY}" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" + +inherit systemd + +# Keep the recipe's source boundary explicit. The core workspace and public +# Rust SDK are staged with the same relative layout they have in the monorepo; +# OS-owned rootfs files are staged separately from application source. +DSTACK_MONOREPO_ROOT ?= "${@os.path.realpath(os.path.join(d.getVar('THISDIR'), '../../../../../..'))}" +DSTACK_CORE_SRC ?= "${DSTACK_MONOREPO_ROOT}/dstack" +DSTACK_RUST_SDK_SRC ?= "${DSTACK_MONOREPO_ROOT}/sdk/rust" +DSTACK_ROOTFS_SRC ?= "${DSTACK_MONOREPO_ROOT}/os/common/rootfs" + +S = "${UNPACKDIR}/repo/dstack" +DSTACK_ROOTFS_FILES = "${UNPACKDIR}/repo/os/common/rootfs" + +RDEPENDS:${PN} += "bash" + +DEPENDS += "rsync-native" + +# Ensure rsync-native is built before unpack runs +do_unpack[depends] += "rsync-native:do_populate_sysroot" + +DSTACK_SERVICES = "dstack-guest-agent.service dstack-guest-agent.socket dstack-prepare.service app-compose.service wg-checker.service" +SYSTEMD_PACKAGES = "${@bb.utils.contains('DISTRO_FEATURES','systemd','${PN}','',d)}" +SYSTEMD_SERVICE:${PN} = "${@bb.utils.contains('DISTRO_FEATURES','systemd','${DSTACK_SERVICES}','',d)}" +SYSTEMD_AUTO_ENABLE:${PN} = "enable" +EXTRA_CARGO_FLAGS = "-p dstack-guest-agent -p dstack-util" + +inherit cargo_bin + +do_unpack() { + install -d "${S}" "${UNPACKDIR}/repo/sdk/rust" "${DSTACK_ROOTFS_FILES}" + rsync -a --exclude=".git" --exclude=".worktrees" --exclude="target" \ + "${DSTACK_CORE_SRC}/" "${S}/" + rsync -a --exclude=".git" --exclude="target" \ + "${DSTACK_RUST_SDK_SRC}/" "${UNPACKDIR}/repo/sdk/rust/" + rsync -a "${DSTACK_ROOTFS_SRC}/" "${DSTACK_ROOTFS_FILES}/" +} + +do_unpack[cleandirs] = "${UNPACKDIR}/repo" + +# Force the configure task to run every time to detect source changes +do_unpack[nostamp] = "1" + +# Add source directory to configure task dependencies +do_unpack[vardeps] += "DSTACK_CORE_SRC DSTACK_RUST_SDK_SRC DSTACK_ROOTFS_SRC" + +do_configure() { + cargo_bin_do_configure +} + +do_compile() { + cargo_bin_do_compile +} + +do_compile[network] = "1" + +do_install() { + install -d ${D}${bindir} + install -d ${D}${sysconfdir}/systemd/journald.conf.d + install -m 0755 ${CARGO_BINDIR}/dstack-util ${D}${bindir} + install -m 0755 ${CARGO_BINDIR}/dstack-guest-agent ${D}${bindir} + install -m 0755 ${DSTACK_ROOTFS_FILES}/dstack-prepare.sh ${D}${bindir} + install -m 0755 ${DSTACK_ROOTFS_FILES}/ephemeral-docker.sh ${D}${bindir} + install -m 0755 ${DSTACK_ROOTFS_FILES}/wg-checker.sh ${D}${bindir} + install -m 0755 ${DSTACK_ROOTFS_FILES}/app-compose.sh ${D}${bindir} + install -m 0644 ${DSTACK_ROOTFS_FILES}/journald.conf ${D}${sysconfdir}/systemd/journald.conf.d/dstack.conf + + install -d ${D}${sysconfdir}/ + install -m 0644 ${DSTACK_ROOTFS_FILES}/tdx-attest.conf ${D}${sysconfdir}/tdx-attest.conf + + install -d ${D}${sysconfdir}/sysctl.d + install -m 0644 ${DSTACK_ROOTFS_FILES}/sysctl.d/99-dstack.conf ${D}${sysconfdir}/sysctl.d/99-dstack.conf + + if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then + install -d ${D}${systemd_system_unitdir} \ + ${D}${sysconfdir}/systemd/resolved.conf.d + + install -m 0644 ${DSTACK_ROOTFS_FILES}/dstack-guest-agent.service ${D}${systemd_system_unitdir} + install -m 0644 ${DSTACK_ROOTFS_FILES}/dstack-prepare.service ${D}${systemd_system_unitdir} + install -m 0644 ${DSTACK_ROOTFS_FILES}/app-compose.service ${D}${systemd_system_unitdir} + install -m 0644 ${DSTACK_ROOTFS_FILES}/wg-checker.service ${D}${systemd_system_unitdir} + install -m 0644 ${DSTACK_ROOTFS_FILES}/dstack-guest-agent.socket ${D}${systemd_system_unitdir} + install -m 0644 ${DSTACK_ROOTFS_FILES}/llmnr.conf ${D}${sysconfdir}/systemd/resolved.conf.d + install -d ${D}${sysconfdir}/systemd/system/docker.service.d + install -m 0644 ${DSTACK_ROOTFS_FILES}/docker.service.d/* ${D}${sysconfdir}/systemd/system/docker.service.d/ + + install -d ${D}${sysconfdir}/systemd/system/containerd.service.d + install -m 0644 ${DSTACK_ROOTFS_FILES}/containerd.service.d/* ${D}${sysconfdir}/systemd/system/containerd.service.d/ + fi +} + +FILES:${PN} += " \ + ${sysconfdir}/systemd/system/docker.service.d/dstack-guest-agent.conf \ + ${sysconfdir}/systemd/system/docker.service.d/dstack-prepare.conf \ + ${sysconfdir}/systemd/system/containerd.service.d/dstack-prepare.conf \ +" + +# Cargo embeds build paths into binaries; allow TMPDIR references. +INSANE_SKIP:${PN} += "buildpaths" +INSANE_SKIP:${PN}-dbg += "buildpaths" diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0001-Update-path-to-native-BaseTools.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0001-Update-path-to-native-BaseTools.patch new file mode 100644 index 000000000..890e58621 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0001-Update-path-to-native-BaseTools.patch @@ -0,0 +1,30 @@ +From 0f859c6de2914cd984f8a0dd61471b91a2371bfa Mon Sep 17 00:00:00 2001 +From: Ricardo Neri +Date: Thu, 9 Jun 2016 02:23:01 -0700 +Subject: [PATCH] ovmf: update path to native BaseTools + +BaseTools is a set of utilities to build EDK-based firmware. These utilities +are used during the build process. Thus, they need to be built natively. +When cross-compiling, we need to provide a path to the location of these +tools. The BBAKE_EDK_TOOLS_PATH string is used as a pattern to be replaced +with the appropriate location before building. + +Signed-off-by: Ricardo Neri +Upstream-Status: Inappropriate [oe-core cross compile specific] +--- + OvmfPkg/build.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/OvmfPkg/build.sh b/OvmfPkg/build.sh +index 279f0d099a..285f061bf4 100755 +--- a/OvmfPkg/build.sh ++++ b/OvmfPkg/build.sh +@@ -24,7 +24,7 @@ then + # this assumes svn pulls have the same root dir + # export EDK_TOOLS_PATH=`pwd`/../BaseTools + # This version is for the tools source in edk2 +- export EDK_TOOLS_PATH=`pwd`/BaseTools ++ export EDK_TOOLS_PATH=BBAKE_EDK_TOOLS_PATH/BaseTools + echo $EDK_TOOLS_PATH + source edksetup.sh BaseTools + else diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0002-BaseTools-makefile-adjust-to-build-in-under-bitbake.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0002-BaseTools-makefile-adjust-to-build-in-under-bitbake.patch new file mode 100644 index 000000000..4a5552857 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0002-BaseTools-makefile-adjust-to-build-in-under-bitbake.patch @@ -0,0 +1,66 @@ +From 9dbd082516fdd7100f99f0f389b8eef5ececab19 Mon Sep 17 00:00:00 2001 +From: Ricardo Neri +Date: Fri, 26 Jul 2019 17:34:26 -0400 +Subject: [PATCH] BaseTools: makefile: adjust to build in under bitbake + +Prepend the build flags with those of bitbake. This is to build +using the bitbake native sysroot include and library directories. + +Note from Alex: this is not appropriate for upstream submission as +the recipe already does lots of similar in-place fixups elsewhere, so +this patch shold be converted to follow that pattern. We're not going +to fight against how upstream wants to configure the build. + +Signed-off-by: Ricardo Neri +Upstream-Status: Inappropriate [needs to be converted to in-recipe fixups] +--- + BaseTools/Source/C/Makefiles/header.makefile | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/BaseTools/Source/C/Makefiles/header.makefile b/BaseTools/Source/C/Makefiles/header.makefile +index 55a7307a5e..e36e6a8f04 100644 +--- a/BaseTools/Source/C/Makefiles/header.makefile ++++ b/BaseTools/Source/C/Makefiles/header.makefile +@@ -154,35 +154,34 @@ endif + + INCLUDE = $(TOOL_INCLUDE) -I $(MAKEROOT) -I $(MAKEROOT)/Include/Common -I $(MAKEROOT)/Include/ -I $(MAKEROOT)/Include/IndustryStandard -I $(MAKEROOT)/Common/ -I .. -I . $(ARCH_INCLUDE) + INCLUDE += -I $(EDK2_PATH)/MdePkg/Include +-CPPFLAGS = $(INCLUDE) ++CPPFLAGS += $(INCLUDE) + + # keep EXTRA_OPTFLAGS last + BUILD_OPTFLAGS = -O2 $(EXTRA_OPTFLAGS) + + ifeq ($(DARWIN),Darwin) + # assume clang or clang compatible flags on OS X +-CFLAGS = -MD -fshort-wchar -fno-strict-aliasing -Wall -Werror \ ++CFLAGS += -MD -fshort-wchar -fno-strict-aliasing -Wall -Werror \ + -Wno-deprecated-declarations -Wno-self-assign -Wno-unused-result -nostdlib -g + else + ifneq ($(CLANG),) +-CFLAGS = -MD -fshort-wchar -fno-strict-aliasing -fwrapv \ ++CFLAGS += -MD -fshort-wchar -fno-strict-aliasing -fwrapv \ + -fno-delete-null-pointer-checks -Wall -Werror \ + -Wno-deprecated-declarations -Wno-self-assign \ + -Wno-unused-result -nostdlib -g + else +-CFLAGS = -MD -fshort-wchar -fno-strict-aliasing -fwrapv \ ++CFLAGS += -MD -fshort-wchar -fno-strict-aliasing -fwrapv \ + -fno-delete-null-pointer-checks -Wall -Werror \ + -Wno-deprecated-declarations -Wno-stringop-truncation -Wno-restrict \ + -Wno-unused-result -nostdlib -g + endif + endif + ifneq ($(CLANG),) +-LDFLAGS = +-CXXFLAGS = -Wno-deprecated-register -Wno-unused-result -std=c++14 ++CXXFLAGS += -Wno-deprecated-register -Wno-unused-result -std=c++14 + else +-LDFLAGS = +-CXXFLAGS = -Wno-unused-result ++CXXFLAGS += -Wno-unused-result + endif ++ + ifeq ($(HOST_ARCH), IA32) + # + # Snow Leopard is a 32-bit and 64-bit environment. uname -m returns i386, but gcc defaults diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0003-Debug-prefix-map.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0003-Debug-prefix-map.patch new file mode 100644 index 000000000..7a665e3ed --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0003-Debug-prefix-map.patch @@ -0,0 +1,91 @@ +From 672d571ed826157e15969b7ba0ec46ab622a7c44 Mon Sep 17 00:00:00 2001 +From: Kevin Wang +Date: Wed, 18 Mar 2026 08:44:20 +0000 +Subject: [PATCH] Debug prefix map +Upstream-Status: Inappropriate [dstack-specific OVMF build/reproducibility tweak] + +--- + BaseTools/Conf/tools_def.template | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/BaseTools/Conf/tools_def.template b/BaseTools/Conf/tools_def.template +index cca699c4a8..e758bd8b4e 100755 +--- a/BaseTools/Conf/tools_def.template ++++ b/BaseTools/Conf/tools_def.template +@@ -920,7 +920,7 @@ NOOPT_*_*_OBJCOPY_ADDDEBUGFLAG = --add-gnu-debuglink="$(DEBUG_DIR)/$(MODULE_ + *_*_*_DTC_PATH = DEF(DTC_BIN) + + # All supported GCC archs except LOONGARCH64 support -mstack-protector-guard=global, so set that on everything except LOONGARCH64 +-DEFINE GCC_ALL_CC_FLAGS = -g -Os -fshort-wchar -fno-builtin -fno-strict-aliasing -Wall -Werror -Wno-array-bounds -include AutoGen.h -fno-common -fstack-protector ++DEFINE GCC_ALL_CC_FLAGS = -g -Os -fshort-wchar -fno-builtin -fno-strict-aliasing -Wall -Werror -Wno-array-bounds -include AutoGen.h -fno-common -fstack-protector ENV(GCC_PREFIX_MAP) + DEFINE GCC_IA32_X64_CC_FLAGS = -mstack-protector-guard=global + DEFINE GCC_ARM_CC_FLAGS = DEF(GCC_ALL_CC_FLAGS) -mlittle-endian -mabi=aapcs -fno-short-enums -funsigned-char -ffunction-sections -fdata-sections -fomit-frame-pointer -Wno-address -mthumb -fno-pic -fno-pie -mstack-protector-guard=global + DEFINE GCC_LOONGARCH64_CC_FLAGS = DEF(GCC_ALL_CC_FLAGS) -mabi=lp64d -fno-asynchronous-unwind-tables -Wno-address -fno-short-enums -fsigned-char -ffunction-sections -fdata-sections +@@ -941,8 +941,8 @@ DEFINE GCC_ARM_ASLDLINK_FLAGS = DEF(GCC_ARM_DLINK_FLAGS) -Wl,--entry,Refere + DEFINE GCC_AARCH64_ASLDLINK_FLAGS = DEF(GCC_AARCH64_DLINK_FLAGS) -Wl,--entry,ReferenceAcpiTable -u $(IMAGE_ENTRY_POINT) DEF(GCC_ARM_AARCH64_ASLDLINK_FLAGS) + DEFINE GCC_LOONGARCH64_ASLDLINK_FLAGS = DEF(GCC_LOONGARCH64_DLINK_FLAGS) -Wl,--entry,ReferenceAcpiTable -u $(IMAGE_ENTRY_POINT) + DEFINE GCC_IA32_X64_DLINK_FLAGS = DEF(GCC_IA32_X64_DLINK_COMMON) --entry _$(IMAGE_ENTRY_POINT) --file-alignment 0x20 --section-alignment 0x20 -Map $(DEST_DIR_DEBUG)/$(BASE_NAME).map +-DEFINE GCC_ASM_FLAGS = -c -x assembler -imacros AutoGen.h +-DEFINE GCC_PP_FLAGS = -E -x assembler-with-cpp -include AutoGen.h ++DEFINE GCC_ASM_FLAGS = -c -x assembler -imacros AutoGen.h ENV(GCC_PREFIX_MAP) ++DEFINE GCC_PP_FLAGS = -E -x assembler-with-cpp -include AutoGen.h ENV(GCC_PREFIX_MAP) + DEFINE GCC_VFRPP_FLAGS = -x c -E -P -DVFRCOMPILE --include $(MODULE_NAME)StrDefs.h + DEFINE GCC_ASLPP_FLAGS = -x c -E -include AutoGen.h + DEFINE GCC_ASLCC_FLAGS = -x c +@@ -1095,7 +1095,7 @@ DEFINE GCC5_LOONGARCH64_PP_FLAGS = -mabi=lp64d -march=loongarch64 DEF( + *_GCC48_IA32_DLINK2_FLAGS = DEF(GCC48_IA32_DLINK2_FLAGS) + *_GCC48_IA32_RC_FLAGS = DEF(GCC_IA32_RC_FLAGS) + *_GCC48_IA32_OBJCOPY_FLAGS = +-*_GCC48_IA32_NASM_FLAGS = -f elf32 ++*_GCC48_IA32_NASM_FLAGS = -f elf32 ENV(NASM_PREFIX_MAP) + + DEBUG_GCC48_IA32_CC_FLAGS = DEF(GCC48_IA32_CC_FLAGS) + RELEASE_GCC48_IA32_CC_FLAGS = DEF(GCC48_IA32_CC_FLAGS) -Wno-unused-but-set-variable +@@ -1123,7 +1123,7 @@ RELEASE_GCC48_IA32_CC_FLAGS = DEF(GCC48_IA32_CC_FLAGS) -Wno-unused-but-set + *_GCC48_X64_DLINK2_FLAGS = DEF(GCC48_X64_DLINK2_FLAGS) + *_GCC48_X64_RC_FLAGS = DEF(GCC_X64_RC_FLAGS) + *_GCC48_X64_OBJCOPY_FLAGS = +-*_GCC48_X64_NASM_FLAGS = -f elf64 ++*_GCC48_X64_NASM_FLAGS = -f elf64 ENV(NASM_PREFIX_MAP) + + DEBUG_GCC48_X64_CC_FLAGS = DEF(GCC48_X64_CC_FLAGS) + RELEASE_GCC48_X64_CC_FLAGS = DEF(GCC48_X64_CC_FLAGS) -Wno-unused-but-set-variable +@@ -1232,7 +1232,7 @@ RELEASE_GCC48_AARCH64_CC_FLAGS = DEF(GCC48_AARCH64_CC_FLAGS) -Wno-unused-but-s + *_GCC49_IA32_DLINK2_FLAGS = DEF(GCC49_IA32_DLINK2_FLAGS) + *_GCC49_IA32_RC_FLAGS = DEF(GCC_IA32_RC_FLAGS) + *_GCC49_IA32_OBJCOPY_FLAGS = +-*_GCC49_IA32_NASM_FLAGS = -f elf32 ++*_GCC49_IA32_NASM_FLAGS = -f elf32 ENV(NASM_PREFIX_MAP) + + DEBUG_GCC49_IA32_CC_FLAGS = DEF(GCC49_IA32_CC_FLAGS) + RELEASE_GCC49_IA32_CC_FLAGS = DEF(GCC49_IA32_CC_FLAGS) -Wno-unused-but-set-variable -Wno-unused-const-variable +@@ -1260,7 +1260,7 @@ RELEASE_GCC49_IA32_CC_FLAGS = DEF(GCC49_IA32_CC_FLAGS) -Wno-unused-but-set + *_GCC49_X64_DLINK2_FLAGS = DEF(GCC49_X64_DLINK2_FLAGS) + *_GCC49_X64_RC_FLAGS = DEF(GCC_X64_RC_FLAGS) + *_GCC49_X64_OBJCOPY_FLAGS = +-*_GCC49_X64_NASM_FLAGS = -f elf64 ++*_GCC49_X64_NASM_FLAGS = -f elf64 ENV(NASM_PREFIX_MAP) + + DEBUG_GCC49_X64_CC_FLAGS = DEF(GCC49_X64_CC_FLAGS) + RELEASE_GCC49_X64_CC_FLAGS = DEF(GCC49_X64_CC_FLAGS) -Wno-unused-but-set-variable -Wno-unused-const-variable +@@ -1519,7 +1519,7 @@ RELEASE_GCCNOLTO_AARCH64_DLINK_XIPFLAGS = -z common-page-size=0x20 + *_GCC5_IA32_DLINK2_FLAGS = DEF(GCC5_IA32_DLINK2_FLAGS) -no-pie + *_GCC5_IA32_RC_FLAGS = DEF(GCC_IA32_RC_FLAGS) + *_GCC5_IA32_OBJCOPY_FLAGS = +-*_GCC5_IA32_NASM_FLAGS = -f elf32 ++*_GCC5_IA32_NASM_FLAGS = -f elf32 ENV(NASM_PREFIX_MAP) + + DEBUG_GCC5_IA32_CC_FLAGS = DEF(GCC5_IA32_CC_FLAGS) -flto + DEBUG_GCC5_IA32_DLINK_FLAGS = DEF(GCC5_IA32_X64_DLINK_FLAGS) -flto -Os -Wl,-m,elf_i386,--oformat=elf32-i386 +@@ -1551,7 +1551,7 @@ RELEASE_GCC5_IA32_DLINK_FLAGS = DEF(GCC5_IA32_X64_DLINK_FLAGS) -flto -Os -Wl, + *_GCC5_X64_DLINK2_FLAGS = DEF(GCC5_X64_DLINK2_FLAGS) + *_GCC5_X64_RC_FLAGS = DEF(GCC_X64_RC_FLAGS) + *_GCC5_X64_OBJCOPY_FLAGS = +-*_GCC5_X64_NASM_FLAGS = -f elf64 ++*_GCC5_X64_NASM_FLAGS = -f elf64 ENV(NASM_PREFIX_MAP) + + DEBUG_GCC5_X64_CC_FLAGS = DEF(GCC5_X64_CC_FLAGS) -flto -DUSING_LTO + DEBUG_GCC5_X64_DLINK_FLAGS = DEF(GCC5_X64_DLINK_FLAGS) -flto -Os +-- +2.43.0 + diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0004-Reproduciable.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0004-Reproduciable.patch new file mode 100644 index 000000000..ab1da4678 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0004-Reproduciable.patch @@ -0,0 +1,157 @@ +From 9133327256392a17883ad3ed91ad63ecbac50f08 Mon Sep 17 00:00:00 2001 +From: Kevin Wang +Date: Wed, 18 Mar 2026 08:44:21 +0000 +Subject: [PATCH] Reproduciable +Upstream-Status: Inappropriate [dstack-specific OVMF build/reproducibility tweak] + +--- + BaseTools/Source/C/GenFw/Elf64Convert.c | 8 ++++--- + .../Source/Python/AutoGen/BuildEngine.py | 3 +++ + BaseTools/Source/Python/AutoGen/GenMake.py | 24 +++++++++---------- + .../Source/Python/AutoGen/ModuleAutoGen.py | 5 +++- + 4 files changed, 24 insertions(+), 16 deletions(-) + +diff --git a/BaseTools/Source/C/GenFw/Elf64Convert.c b/BaseTools/Source/C/GenFw/Elf64Convert.c +index 6919e18809..f6696ae805 100644 +--- a/BaseTools/Source/C/GenFw/Elf64Convert.c ++++ b/BaseTools/Source/C/GenFw/Elf64Convert.c +@@ -15,6 +15,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent + #include + #undef RUNTIME_FUNCTION + #include ++#else ++#define _GNU_SOURCE + #endif + #include + #include +@@ -990,7 +992,7 @@ ScanSections64 ( + } + mCoffOffset = mDebugOffset + sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY) + + sizeof(EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY) + +- strlen(mInImageName) + 1; ++ strlen(basename(mInImageName)) + 1; + + // + // Add more space in the .debug data region for the DllCharacteristicsEx +@@ -2310,7 +2312,7 @@ WriteDebug64 ( + EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *Nb10; + EFI_IMAGE_DEBUG_EX_DLLCHARACTERISTICS_ENTRY *DllEntry; + +- Len = strlen(mInImageName) + 1; ++ Len = strlen(basename(mInImageName)) + 1; + + NtHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(mCoffFile + mNtHdrOffset); + DataDir = &NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]; +@@ -2343,7 +2345,7 @@ WriteDebug64 ( + + Nb10 = (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY*)(Dir + 1); + Nb10->Signature = CODEVIEW_SIGNATURE_NB10; +- strcpy ((char *)(Nb10 + 1), mInImageName); ++ strcpy ((char *)(Nb10 + 1), basename(mInImageName)); + } + + STATIC +diff --git a/BaseTools/Source/Python/AutoGen/BuildEngine.py b/BaseTools/Source/Python/AutoGen/BuildEngine.py +index 45b39d7878..3fed7d1736 100644 +--- a/BaseTools/Source/Python/AutoGen/BuildEngine.py ++++ b/BaseTools/Source/Python/AutoGen/BuildEngine.py +@@ -70,6 +70,9 @@ class TargetDescBlock(object): + else: + return str(Other) == self.Target.Path + ++ def __lt__(self, other): ++ return str(self) < str(other) ++ + def AddInput(self, Input): + if Input not in self.Inputs: + self.Inputs.append(Input) +diff --git a/BaseTools/Source/Python/AutoGen/GenMake.py b/BaseTools/Source/Python/AutoGen/GenMake.py +index 547c708fc7..29859c4a0b 100755 +--- a/BaseTools/Source/Python/AutoGen/GenMake.py ++++ b/BaseTools/Source/Python/AutoGen/GenMake.py +@@ -576,7 +576,7 @@ cleanlib: + os.remove(RespFileList) + + # convert source files and binary files to build targets +- self.ResultFileList = [str(T.Target) for T in MyAgo.CodaTargetList] ++ self.ResultFileList = sorted([str(T.Target) for T in MyAgo.CodaTargetList]) + if len(self.ResultFileList) == 0 and len(MyAgo.SourceFileList) != 0: + EdkLogger.error("build", AUTOGEN_ERROR, "Nothing to build", + ExtraData="[%s]" % str(MyAgo)) +@@ -727,7 +727,7 @@ cleanlib: + OutputFile = '' + DepsFileList = [] + +- for Cmd in self.GenFfsList: ++ for Cmd in sorted(self.GenFfsList): + if Cmd[2]: + for CopyCmd in Cmd[2]: + Src, Dst = CopyCmd +@@ -760,7 +760,7 @@ cleanlib: + self.BuildTargetList.append('\t%s' % CmdString) + + self.ParseSecCmd(DepsFileList, Cmd[1]) +- for SecOutputFile, SecDepsFile, SecCmd in self.FfsOutputFileList : ++ for SecOutputFile, SecDepsFile, SecCmd in sorted(self.FfsOutputFileList): + self.BuildTargetList.append('%s : %s' % (self.ReplaceMacro(SecOutputFile), self.ReplaceMacro(SecDepsFile))) + self.BuildTargetList.append('\t%s' % self.ReplaceMacro(SecCmd)) + self.FfsOutputFileList = [] +@@ -799,13 +799,13 @@ cleanlib: + + def CommandExceedLimit(self): + FlagDict = { +- 'CC' : { 'Macro' : '$(CC_FLAGS)', 'Value' : False}, +- 'PP' : { 'Macro' : '$(PP_FLAGS)', 'Value' : False}, +- 'APP' : { 'Macro' : '$(APP_FLAGS)', 'Value' : False}, +- 'ASLPP' : { 'Macro' : '$(ASLPP_FLAGS)', 'Value' : False}, +- 'VFRPP' : { 'Macro' : '$(VFRPP_FLAGS)', 'Value' : False}, +- 'ASM' : { 'Macro' : '$(ASM_FLAGS)', 'Value' : False}, +- 'ASLCC' : { 'Macro' : '$(ASLCC_FLAGS)', 'Value' : False}, ++ 'CC' : { 'Macro' : '$(CC_FLAGS)', 'Value' : True}, ++ 'PP' : { 'Macro' : '$(PP_FLAGS)', 'Value' : True}, ++ 'APP' : { 'Macro' : '$(APP_FLAGS)', 'Value' : True}, ++ 'ASLPP' : { 'Macro' : '$(ASLPP_FLAGS)', 'Value' : True}, ++ 'VFRPP' : { 'Macro' : '$(VFRPP_FLAGS)', 'Value' : True}, ++ 'ASM' : { 'Macro' : '$(ASM_FLAGS)', 'Value' : True}, ++ 'ASLCC' : { 'Macro' : '$(ASLCC_FLAGS)', 'Value' : True}, + } + + RespDict = {} +@@ -1019,9 +1019,9 @@ cleanlib: + if not self.ObjTargetDict.get(T.Target.SubDir): + self.ObjTargetDict[T.Target.SubDir] = set() + self.ObjTargetDict[T.Target.SubDir].add(NewFile) +- for Type in self._AutoGenObject.Targets: ++ for Type in sorted(self._AutoGenObject.Targets): + resp_file_number = 0 +- for T in self._AutoGenObject.Targets[Type]: ++ for T in sorted(self._AutoGenObject.Targets[Type]): + # Generate related macros if needed + if T.GenFileListMacro and T.FileListMacro not in self.FileListMacros: + self.FileListMacros[T.FileListMacro] = [] +diff --git a/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py b/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py +index 65a2176ca9..318b9611ce 100755 +--- a/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py ++++ b/BaseTools/Source/Python/AutoGen/ModuleAutoGen.py +@@ -1474,6 +1474,9 @@ class ModuleAutoGen(AutoGen): + for File in Files: + if File.lower().endswith('.pdb'): + AsBuiltInfDict['binary_item'].append('DISPOSABLE|' + File) ++ ++ AsBuiltInfDict['binary_item'] = sorted(AsBuiltInfDict['binary_item']) ++ + HeaderComments = self.Module.HeaderComments + StartPos = 0 + for Index in range(len(HeaderComments)): +@@ -1749,7 +1752,7 @@ class ModuleAutoGen(AutoGen): + if os.path.exists (self.TimeStampPath): + os.remove (self.TimeStampPath) + +- SaveFileOnChange(self.TimeStampPath, "\n".join(FileSet), False) ++ SaveFileOnChange(self.TimeStampPath, "\n".join(sorted(FileSet)), False) + + # Ignore generating makefile when it is a binary module + if self.IsBinaryModule: +-- +2.43.0 + diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0005-UefiCpuPkg-CpuExceptionHandlerLib-fix-push-instructi.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0005-UefiCpuPkg-CpuExceptionHandlerLib-fix-push-instructi.patch new file mode 100644 index 000000000..650e084b5 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0005-UefiCpuPkg-CpuExceptionHandlerLib-fix-push-instructi.patch @@ -0,0 +1,46 @@ +From 7e6be0f4068a2158af3c97e873edb33fa4d5c6b8 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 3 Nov 2025 09:56:52 +0100 +Subject: [PATCH 5/5] UefiCpuPkg/CpuExceptionHandlerLib: fix push instructions + +Nasm 3.0 complains about 'dword' being invalid. The comment talks about +a '8-byte value' so 'qword' should be correct here. + +With this change the extra comment explaining that the instruction +actually pushes an 8-byte value despite the 'dword' keyword is not +needed any more. Drop it. + +Fixes: https://github.com/tianocore/edk2/issues/11635 +Signed-off-by: Gerd Hoffmann +Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/9ccf8751a74f26142e584c7b7c7572a182b67997] +(cherry picked from commit 9ccf8751a74f26142e584c7b7c7572a182b67997) +Signed-off-by: Ankur Tyagi +--- + .../CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm +index 3d64ac9080..671ed98f85 100644 +--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm ++++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ExceptionHandlerAsm.nasm +@@ -57,7 +57,7 @@ ALIGN 8 + AsmIdtVectorBegin: + %assign Vector 0 + %rep 256 +- push strict dword %[Vector] ; This instruction pushes sign-extended 8-byte value on stack ++ push strict qword %[Vector] + push rax + %ifdef NO_ABSOLUTE_RELOCS_IN_TEXT + mov rax, strict qword 0 ; mov rax, ASM_PFX(CommonInterruptEntry) +@@ -70,7 +70,7 @@ AsmIdtVectorBegin: + AsmIdtVectorEnd: + + HookAfterStubHeaderBegin: +- push strict dword 0 ; 0 will be fixed ++ push strict qword 0 ; 0 will be fixed + VectorNum: + push rax + %ifdef NO_ABSOLUTE_RELOCS_IN_TEXT +-- +2.47.3 + diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0006-OvmfPkg-AmdSev-drop-embedded-grub.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0006-OvmfPkg-AmdSev-drop-embedded-grub.patch new file mode 100644 index 000000000..365a054ad --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf/0006-OvmfPkg-AmdSev-drop-embedded-grub.patch @@ -0,0 +1,54 @@ +From: dstack +Date: Mon, 16 Jun 2026 00:00:00 +0000 +Subject: [PATCH] OvmfPkg/AmdSev: drop the embedded sevsecret grub + +The AmdSevX64 firmware embeds a grub.efi (built by the OvmfPkg/AmdSev/Grub +PREBUILD via grub-mkimage) that boots an encrypted LUKS volume using the +SEV-injected secret and the out-of-tree sevsecret grub module. + +dstack does not use that boot model: it boots a UKI from an EFI System +Partition with a dm-verity rootfs, so the embedded grub is dead weight. +Building it is also infeasible here: OpenEmbedded provides no x86_64-efi +grub modules at native build time and upstream grub has no sevsecret +module (it is a distro patch). + +Remove the Grub PREBUILD and the Grub.inf component/FV entry. All other +SEV / SEV-SNP functionality (SecretPei/SecretDxe secret injection, the +BlobVerifierLibSevHashes kernel-hashes measured direct boot, SNP support) +is unaffected. + +Upstream-Status: Inappropriate [dstack-specific firmware configuration] +Signed-off-by: dstack +--- +diff --git a/OvmfPkg/AmdSev/AmdSevX64.dsc b/OvmfPkg/AmdSev/AmdSevX64.dsc +index e0eaa3c..76824d8 100644 +--- a/OvmfPkg/AmdSev/AmdSevX64.dsc ++++ b/OvmfPkg/AmdSev/AmdSevX64.dsc +@@ -25,7 +25,6 @@ + BUILD_TARGETS = NOOPT|DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + FLASH_DEFINITION = OvmfPkg/AmdSev/AmdSevX64.fdf +- PREBUILD = sh OvmfPkg/AmdSev/Grub/grub.sh + + # + # Defines for default states. These can be changed on the command line. +@@ -731,7 +730,6 @@ + MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf + + OvmfPkg/AmdSev/SecretDxe/SecretDxe.inf +- OvmfPkg/AmdSev/Grub/Grub.inf + + !include OvmfPkg/Include/Dsc/ShellComponents.dsc.inc + !include OvmfPkg/Include/Dsc/OvmfRngComponents.dsc.inc +diff --git a/OvmfPkg/AmdSev/AmdSevX64.fdf b/OvmfPkg/AmdSev/AmdSevX64.fdf +index 4217870..ff727a5 100644 +--- a/OvmfPkg/AmdSev/AmdSevX64.fdf ++++ b/OvmfPkg/AmdSev/AmdSevX64.fdf +@@ -283,7 +283,6 @@ INF FatPkg/EnhancedFatDxe/Fat.inf + INF MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf + + INF OvmfPkg/AmdSev/SecretDxe/SecretDxe.inf +-INF OvmfPkg/AmdSev/Grub/Grub.inf + + INF MdeModulePkg/Logo/LogoDxe.inf + diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf_git.bb b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf_git.bb new file mode 100644 index 000000000..79806beb0 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-ovmf/dstack-ovmf_git.bb @@ -0,0 +1,332 @@ +SUMMARY = "OVMF - UEFI firmware for Qemu and KVM" +DESCRIPTION = "OVMF is an EDK II based project to enable UEFI support for \ +Virtual Machines. OVMF contains sample UEFI firmware for QEMU and KVM" +HOMEPAGE = "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/tianocore/tianocore.github.io/wiki/OVMF" +LICENSE = "BSD-2-Clause-Patent" +LICENSE:class-target = "${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'BSD-2-Clause-Patent & OpenSSL', 'BSD-2-Clause-Patent', d)}" +LIC_FILES_CHKSUM = "file://OvmfPkg/License.txt;md5=06357ddc23f46577c2aeaeaf7b776d65" + +# Enabling Secure Boot adds a dependency on OpenSSL and implies +# compiling OVMF twice, so it is disabled by default. Distros +# may change that default. +PACKAGECONFIG ??= "" +PACKAGECONFIG += "${@bb.utils.contains('MACHINE_FEATURES', 'tpm', 'tpm', '', d)}" +PACKAGECONFIG += "${@bb.utils.contains('MACHINE_FEATURES', 'tpm2', 'tpm', '', d)}" +PACKAGECONFIG[secureboot] = ",,," +PACKAGECONFIG[tpm] = "-D TPM_ENABLE=TRUE,-D TPM_ENABLE=FALSE,," + +# GCC12 trips on it +#see https://src.fedoraproject.org/rpms/edk2/blob/rawhide/f/0032-Basetools-turn-off-gcc12-warning.patch +BUILD_CFLAGS += "-Wno-error=stringop-overflow" + +SRC_URI = "gitsm://github.com/tianocore/edk2.git;branch=master;protocol=https \ + file://0001-Update-path-to-native-BaseTools.patch \ + file://0002-BaseTools-makefile-adjust-to-build-in-under-bitbake.patch \ + file://0003-Debug-prefix-map.patch \ + file://0004-Reproduciable.patch \ + file://0005-UefiCpuPkg-CpuExceptionHandlerLib-fix-push-instructi.patch \ + file://0006-OvmfPkg-AmdSev-drop-embedded-grub.patch \ + " + +# Pinned to edk2-stable202502 (Feb 2025) instead of the latest stable202505. +# Between these two tags, six commits land in OvmfPkg / MdeModulePkg that +# rewrite the boot-time RTMR[0] event chain: +# fb56dc78ef QemuFwCfgLib: cache + measurement (adds fw_cfg BootMenu, bootorder) +# 45a56d7505 OvmfPkg: add BootManagerMenuApp to dependencies +# 9d9e3a2ba8 OvmfPkg: use BootManagerMenuApp as BootManagerMenu (Boot0000 hash changes) +# d433b4c8e4 PlatformBootManagerLib: register UiApp as optional boot option (new Boot0001) +# dd5cce3e53 PlatformBootManagerCommonLib: set UiApp as an optional boot option +# cd76265f1a OvmfPkg: Enable Smbios measurement (adds EV_EFI_HANDOFF_TABLES whose +# digest is sha384(filtered QEMU SMBIOS table) — varies with -m / -cpu / +# -smbios type=1 and so cannot be precomputed from VmConfig alone) +# stable202502 contains none of them and so produces the same 13-event RTMR[0] +# layout as the legacy 3a3b12cb snapshot dstack used pre-upgrade, while still +# carrying 5 months of post-Sep-2024 EDK2 fixes (incl. CVEs). +# NOTE(wrynose): stable202502 must be KEPT — dstack-mr cannot yet compute +# measurements for newer edk2 (RTMR[0] event chain changed). stable202502 won't +# assemble with wrynose's NASM 3.01 out of the box, so we backport edk2's NASM-3.0 +# CpuExceptionHandlerLib push-instruction fix (0005-UefiCpuPkg-...) to make 202502 +# build while preserving the pre202505 measurement layout dstack-mr expects. +PV = "edk2-stable202502" +SRCREV = "fbe0805b2091393406952e84724188f8c1941837" +UPSTREAM_CHECK_GITTAGREGEX = "(?Pedk2-stable.*)" + +# Tag identifying the OVMF boot-time RTMR[0] event layout this build produces. +# Consumed by mkimage.sh to stamp `ovmf_variant` into the image metadata.json so +# verifiers can pick the matching dstack-mr code path without parsing PV. +# Keep this in sync with the OvmfVariant enum in dstack/dstack-types when +# bumping PV. +OVMF_VARIANT = "pre202505" + +CVE_PRODUCT = "edk2" +CVE_VERSION = "${@d.getVar('PV').split('-')[1]}" + +CVE_STATUS[CVE-2014-8271] = "fixed-version: Fixed in svn_16280, which is an unusual versioning breaking version comparison." +CVE_STATUS[CVE-2014-4859] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2014-4860] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14553] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14559] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14562] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14563] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14575] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14586] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." +CVE_STATUS[CVE-2019-14587] = "fixed-version: The CPE in the NVD database doesn't reflect correctly the vulnerable versions." + +inherit deploy + +PARALLEL_MAKE = "" + + +DEPENDS = "nasm-native acpica-native ovmf-native util-linux-native" + +# Build the AMD SEV firmware in addition to the Intel TDX one. The TDX build +# (IntelTdxX64.dsc) and its carefully pinned measurement layout are left byte +# for byte unchanged; this only adds a separate ovmf-sev.fd artifact. The +# AmdSevX64 embedded grub (sevsecret LUKS boot) is removed via +# 0006-OvmfPkg-AmdSev-drop-embedded-grub.patch -- dstack boots a UKI, not +# grub, and that grub cannot be built here anyway (OE has no x86_64-efi grub +# modules / no sevsecret). The patch fails loud if a future edk2 bump changes +# the AmdSev layout. +OVMF_BUILD_SEV ??= "1" + +EDK_TOOLS_DIR="edk2_basetools" + +# OVMF has trouble building with the default optimization of -O2. +BUILD_OPTIMIZATION = "" + +# OVMF supports IA only, although it could conceivably support ARM someday. +COMPATIBLE_HOST:class-target='(i.86|x86_64).*' + +# Additional build flags for OVMF with Secure Boot. +# Fedora also uses "-D SMM_REQUIRE -D EXCLUDE_SHELL_FROM_FD". +OVMF_SECURE_BOOT_EXTRA_FLAGS ??= "" +OVMF_SECURE_BOOT_FLAGS = "-DSECURE_BOOT_ENABLE=TRUE ${OVMF_SECURE_BOOT_EXTRA_FLAGS}" + +export PYTHON_COMMAND = "${HOSTTOOLS_DIR}/python3" + +do_patch[postfuncs] += "fix_basetools_location" +fix_basetools_location () { +} +fix_basetools_location:class-target() { + # Replaces the fake path inserted by 0002-ovmf-update-path-to-native-BaseTools.patch. + # Necessary for finding the actual BaseTools from ovmf-native. + sed -i -e 's#BBAKE_EDK_TOOLS_PATH#${STAGING_BINDIR_NATIVE}/${EDK_TOOLS_DIR}#' ${S}/OvmfPkg/build.sh +} + +do_patch[postfuncs] += "fix_iasl" +fix_iasl() { +} +fix_iasl:class-native() { + # iasl is not installed under /usr/bin when building with OE. + sed -i -e 's#/usr/bin/iasl#${STAGING_BINDIR_NATIVE}/iasl#' ${S}/BaseTools/Conf/tools_def.template +} + +# Inject CC and friends into the build. LINKER already is in GNUmakefile. +# Must be idempotent and thus remove old assignments that were inserted +# earlier. +do_patch[postfuncs] += "fix_toolchain" +fix_toolchain() { + sed -i \ + -e '/^\(CC\|CXX\|AS\|AR\|LD\|LINKER\) =/d' \ + -e '/^APPLICATION/a CC = ${CC}\nCXX = ${CXX}\nAS = ${AS}\nAR = ${AR}\nLD = ${LD}\nLINKER = $(CC)' \ + ${S}/BaseTools/Source/C/Makefiles/app.makefile + sed -i \ + -e '/^\(CC\|CXX\|AS\|AR\|LD\)/d' \ + -e '/^VFR_CPPFLAGS/a CC = ${CC}\nCXX = ${CXX}\nAS = ${AS}\nAR = ${AR}\nLD = ${LD}' \ + ${S}/BaseTools/Source/C/VfrCompile/GNUmakefile +} +fix_toolchain:append:class-native() { + # This tools_def.template is going to be used by the target ovmf and + # defines which compilers to use. For the GCC toolchain definitions, + # that will be ${HOST_PREFIX}gcc. However, "make" doesn't need that + # prefix. + # + # Injecting ENV(HOST_PREFIX) matches exporting that value as env + # variable in do_compile:class-target. + sed -i \ + -e 's#\(ENV\|DEF\)(GCC.*_PREFIX)#ENV(HOST_PREFIX)#' \ + -e 's#ENV(HOST_PREFIX)make#make#' \ + ${S}/BaseTools/Conf/tools_def.template + sed -i \ + -e '/^\(LFLAGS\|CFLAGS\) +=/d' \ + -e '/^LINKER/a LFLAGS += ${BUILD_LDFLAGS}\nCFLAGS += ${BUILD_CFLAGS}' \ + ${S}/BaseTools/Source/C/Makefiles/app.makefile \ + ${S}/BaseTools/Source/C/VfrCompile/GNUmakefile + # Linking with gold fails: + # internal error in do_layout, at ../../gold/object.cc:1821 + # make: *** [.../OUTPUT/Facs.acpi] Error 1 + # We intentionally hard-code the use of ld.bfd regardless of DISTRO_FEATURES + # to make ovmf-native reusable across distros. + sed -i \ + -e 's#^\(DEFINE GCC.*DLINK.*FLAGS *=\)#\1 -fuse-ld=bfd#' \ + -e 's#-flto#-fno-lto#g' \ + -e 's#-DUSING_LTO##g' \ + ${S}/BaseTools/Conf/tools_def.template +} + +# We disable lto above since the results are not reproducible and make it hard to compare +# binary build aretfacts to debug reproducibility problems. +# Surprisingly, if you disable lto, you see compiler warnings which are fatal. We therefore +# have to hack warnings overrides into GCC_PREFIX_MAP to allow it to build. + +# We want to pass ${DEBUG_PREFIX_MAP} to gcc commands and also pass in +# --debug-prefix-map to nasm (we carry a patch to nasm for this). The +# tools definitions are built by ovmf-native so we need to pass this in +# at target build time when we know the right values. +export NASM_PREFIX_MAP = "--debug-prefix-map=${WORKDIR}=${TARGET_DBGSRC_DIR}" +export GCC_PREFIX_MAP = "${DEBUG_PREFIX_MAP} -Wno-stringop-overflow -Wno-maybe-uninitialized" + +GCC_VER="$(${CC} -v 2>&1 | tail -n1 | awk '{print $3}')" + +fixup_target_tools() { + case ${1} in + 4.4.*) + FIXED_GCCVER=GCC44 + ;; + 4.5.*) + FIXED_GCCVER=GCC45 + ;; + 4.6.*) + FIXED_GCCVER=GCC46 + ;; + 4.7.*) + FIXED_GCCVER=GCC47 + ;; + 4.8.*) + FIXED_GCCVER=GCC48 + ;; + 4.9.*) + FIXED_GCCVER=GCC49 + ;; + *) + FIXED_GCCVER=GCC5 + ;; + esac + echo ${FIXED_GCCVER} +} + +do_compile:class-native() { + oe_runmake -C ${S}/BaseTools +} + +do_compile:class-target() { + export LFLAGS="${LDFLAGS}" + PARALLEL_JOBS="${@oe.utils.parallel_make_argument(d, '-n %d')}" + OVMF_ARCH="X64" + if [ "${TARGET_ARCH}" != "x86_64" ] ; then + OVMF_ARCH="IA32" + fi + + # The build for the target uses BaseTools/Conf/tools_def.template + # from ovmf-native to find the compiler, which depends on + # exporting HOST_PREFIX. + export HOST_PREFIX="${HOST_PREFIX}" + + # BaseTools/Conf gets copied to Conf, but only if that does not + # exist yet. To ensure that an updated template gets used during + # incremental builds, we need to remove the copy before we start. + rm -f `ls ${S}/Conf/*.txt | grep -v ReadMe.txt` + + # ${WORKDIR}/ovmf is a well-known location where do_install and + # do_deploy will be able to find the files. + rm -rf ${WORKDIR}/ovmf + mkdir ${WORKDIR}/ovmf + FIXED_GCCVER=$(fixup_target_tools ${GCC_VER}) + bbnote FIXED_GCCVER is ${FIXED_GCCVER} + build_dir="${S}/Build/IntelTdx/RELEASE_${FIXED_GCCVER}" + + bbnote "Building without Secure Boot." + rm -rf ${S}/Build/IntelTdx + ${S}/OvmfPkg/build.sh -p ${S}/OvmfPkg/IntelTdx/IntelTdxX64.dsc $PARALLEL_JOBS -a $OVMF_ARCH -b RELEASE -t ${FIXED_GCCVER} ${PACKAGECONFIG_CONFARGS} + ln ${build_dir}/FV/OVMF.fd ${WORKDIR}/ovmf/ovmf.fd + ln ${build_dir}/FV/OVMF_CODE.fd ${WORKDIR}/ovmf/ovmf.code.fd + ln ${build_dir}/FV/OVMF_VARS.fd ${WORKDIR}/ovmf/ovmf.vars.fd + ln ${build_dir}/${OVMF_ARCH}/Shell.efi ${WORKDIR}/ovmf/ + + if ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'true', 'false', d)}; then + # Repeat build with the Secure Boot flags. + bbnote "Building with Secure Boot." + rm -rf ${S}/Build/IntelTdx + ${S}/OvmfPkg/build.sh -p ${S}/OvmfPkg/IntelTdx/IntelTdxX64.dsc $PARALLEL_JOBS -a $OVMF_ARCH -b RELEASE -t ${FIXED_GCCVER} ${PACKAGECONFIG_CONFARGS} ${OVMF_SECURE_BOOT_FLAGS} + ln ${build_dir}/FV/OVMF.fd ${WORKDIR}/ovmf/ovmf.secboot.fd + ln ${build_dir}/FV/OVMF_CODE.fd ${WORKDIR}/ovmf/ovmf.secboot.code.fd + ln ${build_dir}/${OVMF_ARCH}/EnrollDefaultKeys.efi ${WORKDIR}/ovmf/ + fi + + if [ "${OVMF_BUILD_SEV}" = "1" ]; then + # AMD SEV / SEV-SNP firmware. Additive: produces a single combined + # firmware blob (used via QEMU -bios) at ovmf-sev.fd, leaving the TDX + # build above untouched. The embedded grub is stripped below, so + # there is no PREBUILD / grub toolchain dependency. + bbnote "Building AMD SEV firmware (AmdSevX64.dsc)." + sev_build_dir="${S}/Build/AmdSev/RELEASE_${FIXED_GCCVER}" + rm -rf ${S}/Build/AmdSev + ${S}/OvmfPkg/build.sh -p ${S}/OvmfPkg/AmdSev/AmdSevX64.dsc $PARALLEL_JOBS -a $OVMF_ARCH -b RELEASE -t ${FIXED_GCCVER} ${PACKAGECONFIG_CONFARGS} + ln ${sev_build_dir}/FV/OVMF.fd ${WORKDIR}/ovmf/ovmf-sev.fd + fi +} + +do_install:class-native() { + install -d ${D}/${bindir}/edk2_basetools + find ${S}/BaseTools -name \*.pyc -exec rm -rf \{\} \; + cp -r ${S}/BaseTools ${D}/${bindir}/${EDK_TOOLS_DIR} +} + +do_install:class-target() { + # Content for UEFI shell iso. We install the EFI shell as + # bootx64/ia32.efi because then it can be started even when the + # firmware itself does not contain it. + install -d ${D}/efi/boot + install ${WORKDIR}/ovmf/Shell.efi ${D}/efi/boot/boot${@ "ia32" if "${TARGET_ARCH}" != "x86_64" else "x64"}.efi + if ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'true', 'false', d)}; then + install ${WORKDIR}/ovmf/EnrollDefaultKeys.efi ${D} + fi +} + +# This always gets packaged because ovmf-shell-image depends on it. +# This allows testing that recipe in all configurations because it +# can always be part of a world build. +# +# However, EnrollDefaultKeys.efi is only included when Secure Boot is enabled. +PACKAGES =+ "ovmf-shell-efi" +FILES:ovmf-shell-efi = " \ + EnrollDefaultKeys.efi \ + efi/ \ +" + +DEPLOYDEP = "" +DEPLOYDEP:class-target = "qemu-system-native:do_populate_sysroot" +DEPLOYDEP:class-target += " ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'openssl-native:do_populate_sysroot', '', d)}" +do_deploy[depends] += "${DEPLOYDEP}" + +do_deploy() { +} +do_deploy:class-target() { + # For use with "runqemu ovmf". + for i in \ + ovmf \ + ovmf.code \ + ovmf.vars \ + ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'ovmf.secboot ovmf.secboot.code', '', d)} \ + ; do + cp ${WORKDIR}/ovmf/$i.fd ${DEPLOYDIR}/ + qemu-img convert -f raw -O qcow2 ${WORKDIR}/ovmf/$i.fd ${DEPLOYDIR}/$i.qcow2 + done + + # AMD SEV firmware (single combined blob for QEMU -bios). + if [ "${OVMF_BUILD_SEV}" = "1" ] && [ -f ${WORKDIR}/ovmf/ovmf-sev.fd ]; then + cp ${WORKDIR}/ovmf/ovmf-sev.fd ${DEPLOYDIR}/ + qemu-img convert -f raw -O qcow2 ${WORKDIR}/ovmf/ovmf-sev.fd ${DEPLOYDIR}/ovmf-sev.qcow2 + fi + + if ${@bb.utils.contains('PACKAGECONFIG', 'secureboot', 'true', 'false', d)}; then + # Create a test Platform Key and first Key Exchange Key to use with EnrollDefaultKeys + openssl req -new -x509 -newkey rsa:2048 -keyout ${DEPLOYDIR}/OvmfPkKek1.key \ + -out ${DEPLOYDIR}/OvmfPkKek1.crt -nodes -days 20 -subj "/CN=OVMFSecBootTest" + openssl x509 -in ${DEPLOYDIR}/OvmfPkKek1.crt -out ${DEPLOYDIR}/OvmfPkKek1.pem -outform PEM + fi +} +addtask do_deploy after do_compile before do_build + +BBCLASSEXTEND = "native" +TOOLCHAIN = "gcc" diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/dstack-sysbox_0.6.7.bb b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/dstack-sysbox_0.6.7.bb new file mode 100644 index 000000000..9415bd206 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/dstack-sysbox_0.6.7.bb @@ -0,0 +1,181 @@ +SUMMARY = "Sysbox container runtime" +DESCRIPTION = "An open-source, next-generation runc that empowers rootless containers \ +to run workloads such as Systemd, Docker, Kubernetes, just like VMs." +HOMEPAGE = "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/nestybox/sysbox" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=cf0915b5e4f1337cf5b929ba1e388c42" + +SYSBOX_VERSION = "0.6.7" + +# Pin all submodule revisions from the v0.6.7 tag for reproducibility. +SRCREV_sysbox = "3a69811f54f8f83264ebb36dcaf51708e80b9e84" +SRCREV_sysbox-runc = "c58eba1be027c762c495bc4eeba7c0984beda1ab" +SRCREV_sysbox-fs = "6a8d71f54e7570e5297af89ff24ed3bafa61659f" +SRCREV_sysbox-mgr = "aaeff6c5dc70c137e62166474a309ca5fc42d044" +SRCREV_sysbox-ipc = "f05151f4b4c1df63d7fd241577ca032905c1bd0e" +SRCREV_sysbox-libs = "6faf00c74d45f7bdff0d1930fe8590c17a2d0a8b" +# bazil/fuse commit 45cd9a3 is orphaned in nestybox/fuse (not on any branch/tag). +# We forked it to Dstack-TEE/fuse with a named branch so BitBake can fetch it. +SRCREV_sysbox-fuse = "45cd9a3d884448418546d8eaa54ee7d772e576d6" + +SRCREV_FORMAT = "sysbox" + +SRC_URI = " \ + git://github.com/nestybox/sysbox.git;nobranch=1;name=sysbox;protocol=https;destsuffix=sysbox \ + git://github.com/nestybox/sysbox-runc.git;nobranch=1;name=sysbox-runc;protocol=https;destsuffix=sysbox-runc \ + git://github.com/nestybox/sysbox-fs.git;nobranch=1;name=sysbox-fs;protocol=https;destsuffix=sysbox-fs \ + git://github.com/nestybox/sysbox-mgr.git;nobranch=1;name=sysbox-mgr;protocol=https;destsuffix=sysbox-mgr \ + git://github.com/nestybox/sysbox-ipc.git;nobranch=1;name=sysbox-ipc;protocol=https;destsuffix=sysbox-ipc \ + git://github.com/nestybox/sysbox-libs.git;nobranch=1;name=sysbox-libs;protocol=https;destsuffix=sysbox-libs \ + git://github.com/Dstack-TEE/fuse.git;branch=sysbox-v0.6.7;name=sysbox-fuse;protocol=https;destsuffix=sysbox-fuse \ + file://sysbox.service \ + file://sysbox-fs.service \ + file://sysbox-mgr.service \ + file://99-sysbox-sysctl.conf \ + file://50-sysbox-mod.conf \ + file://sysboxFsProtobuf.pb.go \ + file://sysboxMgrProtobuf.pb.go \ +" + +S = "${UNPACKDIR}/sysbox" + +PV = "${SYSBOX_VERSION}+git${SRCPV}" + +DEPENDS += "libseccomp" +RDEPENDS:${PN} += "libseccomp rsync fuse" + +inherit go goarch pkgconfig systemd + +GO_IMPORT = "github.com/nestybox/sysbox" + +SYSBOX_LDFLAGS = " \ + -X 'main.edition=Community Edition (CE)' \ + -X main.version=${SYSBOX_VERSION} \ + -X main.commitId=${SRCREV_sysbox} \ + -X 'main.builtAt=1970-01-01T00:00:00Z' \ + -X 'main.builtBy=dstack' \ +" + +# Kernel >= 5.12 supports idmapped mounts +SYSBOX_RUNC_BUILDTAGS = "seccomp idmapped_mnt" +SYSBOX_MGR_BUILDTAGS = "idmapped_mnt" + +do_configure() { + # Arrange the source tree so that go.mod replace directives work. + # All components expect to find siblings in ../ relative to themselves. + # The git fetcher places them in ${UNPACKDIR}/sysbox-{runc,fs,mgr,ipc,libs}. + # This is already the correct layout since they are all at the same level + # under ${UNPACKDIR}. + + # sysbox-fs expects a 'bazil' subdirectory (submodule of nestybox/fuse). + # Remove the empty submodule placeholder left by git checkout, then symlink. + rm -rf ${UNPACKDIR}/sysbox-fs/bazil + ln -sfn ${UNPACKDIR}/sysbox-fuse ${UNPACKDIR}/sysbox-fs/bazil + + # Install pre-generated protobuf Go files. The upstream repo only ships + # .proto files and expects protoc + protoc-gen-go at build time. We + # pre-generate them to avoid the protoc native toolchain dependency. + install -m 0644 ${UNPACKDIR}/sysboxFsProtobuf.pb.go \ + ${UNPACKDIR}/sysbox-ipc/sysboxFsGrpc/sysboxFsProtobuf/ + install -m 0644 ${UNPACKDIR}/sysboxMgrProtobuf.pb.go \ + ${UNPACKDIR}/sysbox-ipc/sysboxMgrGrpc/sysboxMgrProtobuf/ + + # Vendor dependencies for each component so that do_compile needs no + # network access. go.sum in each repo guarantees content integrity. + # Use -modcacherw so cached modules are writable (BitBake needs to + # clean ${B}/pkg/mod between tasks). + for mod in sysbox-runc sysbox-fs sysbox-mgr; do + cd ${UNPACKDIR}/$mod + ${GO} mod vendor -modcacherw + done +} + +do_configure[network] = "1" + +do_compile() { + export CGO_ENABLED="1" + export CGO_CFLAGS="${CFLAGS} --sysroot=${STAGING_DIR_TARGET}" + export CGO_LDFLAGS="${LDFLAGS} -Wl,--build-id=none --sysroot=${STAGING_DIR_TARGET}" + export CFLAGS="" + export LDFLAGS="" + + # Set reproducible build environment + export SOURCE_DATE_EPOCH=0 + export TZ=UTC + + # Build sysbox-runc + cd ${UNPACKDIR}/sysbox-runc + ${GO} build -mod=vendor -buildvcs=false -trimpath \ + -tags "${SYSBOX_RUNC_BUILDTAGS}" \ + -ldflags "-buildid= -s -w -linkmode external -extldflags '-Wl,--build-id=none' ${SYSBOX_LDFLAGS}" \ + -o ${WORKDIR}/sysbox-runc-bin . + + # Build sysbox-fs + cd ${UNPACKDIR}/sysbox-fs + ${GO} build -mod=vendor -buildvcs=false -trimpath \ + -ldflags "-buildid= -s -w -linkmode external -extldflags '-Wl,--build-id=none' ${SYSBOX_LDFLAGS}" \ + -o ${WORKDIR}/sysbox-fs-bin ./cmd/sysbox-fs + + # Build sysbox-mgr + cd ${UNPACKDIR}/sysbox-mgr + ${GO} build -mod=vendor -buildvcs=false -trimpath \ + -tags "${SYSBOX_MGR_BUILDTAGS}" \ + -ldflags "-buildid= -s -w -linkmode external -extldflags '-Wl,--build-id=none' ${SYSBOX_LDFLAGS}" \ + -o ${WORKDIR}/sysbox-mgr-bin . +} + +do_install() { + # Install binaries + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/sysbox-runc-bin ${D}${bindir}/sysbox-runc + install -m 0755 ${WORKDIR}/sysbox-fs-bin ${D}${bindir}/sysbox-fs + install -m 0755 ${WORKDIR}/sysbox-mgr-bin ${D}${bindir}/sysbox-mgr + + # Install systemd services + if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then + install -d ${D}${systemd_system_unitdir} + install -m 0644 ${UNPACKDIR}/sysbox.service ${D}${systemd_system_unitdir} + install -m 0644 ${UNPACKDIR}/sysbox-fs.service ${D}${systemd_system_unitdir} + install -m 0644 ${UNPACKDIR}/sysbox-mgr.service ${D}${systemd_system_unitdir} + fi + + # Install sysctl config + install -d ${D}${sysconfdir}/sysctl.d + install -m 0644 ${UNPACKDIR}/99-sysbox-sysctl.conf ${D}${sysconfdir}/sysctl.d/ + + # Install module autoload config + install -d ${D}${sysconfdir}/modules-load.d + install -m 0644 ${UNPACKDIR}/50-sysbox-mod.conf ${D}${sysconfdir}/modules-load.d/ + + # Create sysbox data directory + install -d ${D}/var/lib/sysbox +} + +SYSTEMD_PACKAGES = "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', '${PN}', '', d)}" +SYSTEMD_SERVICE:${PN} = "sysbox.service sysbox-fs.service sysbox-mgr.service" +SYSTEMD_AUTO_ENABLE:${PN} = "enable" + +FILES:${PN} += " \ + ${bindir}/sysbox-runc \ + ${bindir}/sysbox-fs \ + ${bindir}/sysbox-mgr \ + ${systemd_system_unitdir}/sysbox.service \ + ${systemd_system_unitdir}/sysbox-fs.service \ + ${systemd_system_unitdir}/sysbox-mgr.service \ + ${sysconfdir}/sysctl.d/99-sysbox-sysctl.conf \ + ${sysconfdir}/modules-load.d/50-sysbox-mod.conf \ + /var/lib/sysbox \ +" + +# Pre-create subuid/subgid entries for sysbox user namespace mappings. +# sysbox-mgr tries to write these at startup, but rootfs is read-only (dm-verity). +# If the correct entry already exists, sysbox-mgr skips the write. +# This runs at rootfs creation time (not first boot). +pkg_postinst:${PN}() { + echo "sysbox:100000:65536" >> $D${sysconfdir}/subuid + echo "sysbox:100000:65536" >> $D${sysconfdir}/subgid +} + +INSANE_SKIP:${PN} += "ldflags already-stripped" + +COMPATIBLE_HOST = "x86_64.*-linux" diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/50-sysbox-mod.conf b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/50-sysbox-mod.conf new file mode 100644 index 000000000..214e86b00 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/50-sysbox-mod.conf @@ -0,0 +1 @@ +configfs diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/99-sysbox-sysctl.conf b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/99-sysbox-sysctl.conf new file mode 100644 index 000000000..17cccab61 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/99-sysbox-sysctl.conf @@ -0,0 +1,19 @@ +# Sysbox recommended sysctl settings +# See: https://github.com/nestybox/sysbox/blob/master/docs/user-guide/install-package.md + +# Allow unprivileged users to create user namespaces (required for rootless containers) +kernel.unprivileged_userns_clone = 1 + +# Raise inotify limits — each sysbox container runs its own init system (e.g. systemd) +# which uses inotify heavily; defaults (8192/128) exhaust quickly with multiple containers +fs.inotify.max_queued_events = 1048576 +fs.inotify.max_user_watches = 1048576 +fs.inotify.max_user_instances = 1048576 + +# Raise kernel keyring limits — each user namespace consumes kernel keys for UID/GID mappings +kernel.keys.maxkeys = 20000 +kernel.keys.maxbytes = 1400000 + +# Raise max PID — sysbox containers share the host PID space; default 32768 exhausts +# quickly with many containers each running their own process trees +kernel.pid_max = 4194304 diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox-fs.service b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox-fs.service new file mode 100644 index 000000000..606f90408 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox-fs.service @@ -0,0 +1,20 @@ +[Unit] +Description=sysbox-fs (part of the Sysbox container runtime) +PartOf=sysbox.service +After=sysbox-mgr.service + +[Service] +Type=notify +ExecStart=/usr/bin/sysbox-fs +TimeoutStartSec=10 +TimeoutStopSec=10 +StartLimitInterval=0 +NotifyAccess=main +OOMScoreAdjust=-500 +LimitNOFILE=infinity +LimitNPROC=infinity +StandardOutput=journal+console +StandardError=journal+console + +[Install] +WantedBy=sysbox.service diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox-mgr.service b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox-mgr.service new file mode 100644 index 000000000..8bcfcd026 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox-mgr.service @@ -0,0 +1,20 @@ +[Unit] +Description=sysbox-mgr (part of the Sysbox container runtime) +PartOf=sysbox.service +After=dstack-prepare.service + +[Service] +Type=notify +ExecStart=/usr/bin/sysbox-mgr --data-root /var/lib/sysbox --disable-ovfs-on-idmapped-mount +TimeoutStartSec=45 +TimeoutStopSec=90 +StartLimitInterval=0 +NotifyAccess=main +OOMScoreAdjust=-500 +LimitNOFILE=infinity +LimitNPROC=infinity +StandardOutput=journal+console +StandardError=journal+console + +[Install] +WantedBy=sysbox.service diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox.service b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox.service new file mode 100644 index 000000000..d6605f3a9 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysbox.service @@ -0,0 +1,14 @@ +[Unit] +Description=Sysbox container runtime +Documentation=https://github.com/nestybox/sysbox +Wants=dstack-prepare.service +After=dstack-prepare.service sysbox-mgr.service sysbox-fs.service +BindsTo=sysbox-mgr.service sysbox-fs.service +Before=docker.service containerd.service + +[Service] +Type=exec +ExecStart=/bin/sh -c "/usr/bin/sysbox-runc --version && /usr/bin/sysbox-mgr --version && /usr/bin/sysbox-fs --version && /bin/sleep infinity" + +[Install] +WantedBy=multi-user.target diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysboxFsProtobuf.pb.go b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysboxFsProtobuf.pb.go new file mode 100644 index 000000000..d3b0a668f --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysboxFsProtobuf.pb.go @@ -0,0 +1,703 @@ +// +// Sysbox-fs Protobuffer Definitions. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v3.21.12 +// source: sysboxFsProtobuf.proto + +package sysboxFsProtobuf + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Request message sent by runC to sysbox-fs process during container +// registration, unregistration and update phases. +type ContainerData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"` + InitPid int32 `protobuf:"varint,2,opt,name=InitPid,proto3" json:"InitPid,omitempty"` + Hostname string `protobuf:"bytes,3,opt,name=Hostname,proto3" json:"Hostname,omitempty"` + Ctime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=Ctime,proto3" json:"Ctime,omitempty"` + UidFirst int32 `protobuf:"varint,5,opt,name=UidFirst,proto3" json:"UidFirst,omitempty"` + UidSize int32 `protobuf:"varint,6,opt,name=UidSize,proto3" json:"UidSize,omitempty"` + GidFirst int32 `protobuf:"varint,7,opt,name=GidFirst,proto3" json:"GidFirst,omitempty"` + GidSize int32 `protobuf:"varint,8,opt,name=GidSize,proto3" json:"GidSize,omitempty"` + ProcRoPaths []string `protobuf:"bytes,9,rep,name=ProcRoPaths,proto3" json:"ProcRoPaths,omitempty"` + ProcMaskPaths []string `protobuf:"bytes,10,rep,name=ProcMaskPaths,proto3" json:"ProcMaskPaths,omitempty"` + Netns string `protobuf:"bytes,11,opt,name=Netns,proto3" json:"Netns,omitempty"` +} + +func (x *ContainerData) Reset() { + *x = ContainerData{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxFsProtobuf_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerData) ProtoMessage() {} + +func (x *ContainerData) ProtoReflect() protoreflect.Message { + mi := &file_sysboxFsProtobuf_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerData.ProtoReflect.Descriptor instead. +func (*ContainerData) Descriptor() ([]byte, []int) { + return file_sysboxFsProtobuf_proto_rawDescGZIP(), []int{0} +} + +func (x *ContainerData) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ContainerData) GetInitPid() int32 { + if x != nil { + return x.InitPid + } + return 0 +} + +func (x *ContainerData) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *ContainerData) GetCtime() *timestamppb.Timestamp { + if x != nil { + return x.Ctime + } + return nil +} + +func (x *ContainerData) GetUidFirst() int32 { + if x != nil { + return x.UidFirst + } + return 0 +} + +func (x *ContainerData) GetUidSize() int32 { + if x != nil { + return x.UidSize + } + return 0 +} + +func (x *ContainerData) GetGidFirst() int32 { + if x != nil { + return x.GidFirst + } + return 0 +} + +func (x *ContainerData) GetGidSize() int32 { + if x != nil { + return x.GidSize + } + return 0 +} + +func (x *ContainerData) GetProcRoPaths() []string { + if x != nil { + return x.ProcRoPaths + } + return nil +} + +func (x *ContainerData) GetProcMaskPaths() []string { + if x != nil { + return x.ProcMaskPaths + } + return nil +} + +func (x *ContainerData) GetNetns() string { + if x != nil { + return x.Netns + } + return "" +} + +// Response message sent from sysbox-fs to runC process. +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=Success,proto3" json:"Success,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxFsProtobuf_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_sysboxFsProtobuf_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_sysboxFsProtobuf_proto_rawDescGZIP(), []int{1} +} + +func (x *Response) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type MountpointReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *MountpointReq) Reset() { + *x = MountpointReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxFsProtobuf_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountpointReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountpointReq) ProtoMessage() {} + +func (x *MountpointReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxFsProtobuf_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountpointReq.ProtoReflect.Descriptor instead. +func (*MountpointReq) Descriptor() ([]byte, []int) { + return file_sysboxFsProtobuf_proto_rawDescGZIP(), []int{2} +} + +type MountpointResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mountpoint string `protobuf:"bytes,1,opt,name=Mountpoint,proto3" json:"Mountpoint,omitempty"` +} + +func (x *MountpointResp) Reset() { + *x = MountpointResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxFsProtobuf_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountpointResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountpointResp) ProtoMessage() {} + +func (x *MountpointResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxFsProtobuf_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountpointResp.ProtoReflect.Descriptor instead. +func (*MountpointResp) Descriptor() ([]byte, []int) { + return file_sysboxFsProtobuf_proto_rawDescGZIP(), []int{3} +} + +func (x *MountpointResp) GetMountpoint() string { + if x != nil { + return x.Mountpoint + } + return "" +} + +var File_sysboxFsProtobuf_proto protoreflect.FileDescriptor + +var file_sysboxFsProtobuf_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x73, 0x79, 0x73, 0x62, 0x6f, 0x78, 0x46, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x02, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x69, 0x74, 0x50, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x49, 0x6e, 0x69, 0x74, 0x50, 0x69, 0x64, 0x12, + 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x43, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x43, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x55, 0x69, 0x64, 0x46, 0x69, 0x72, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x55, 0x69, 0x64, 0x46, 0x69, 0x72, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x55, 0x69, 0x64, + 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x55, 0x69, 0x64, 0x53, + 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x47, 0x69, 0x64, 0x46, 0x69, 0x72, 0x73, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x47, 0x69, 0x64, 0x46, 0x69, 0x72, 0x73, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x47, 0x69, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x07, 0x47, 0x69, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, + 0x63, 0x52, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, + 0x50, 0x72, 0x6f, 0x63, 0x52, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x50, + 0x72, 0x6f, 0x63, 0x4d, 0x61, 0x73, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0a, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x50, 0x72, 0x6f, 0x63, 0x4d, 0x61, 0x73, 0x6b, 0x50, 0x61, 0x74, 0x68, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x4e, 0x65, 0x74, 0x6e, 0x73, 0x22, 0x24, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x0f, 0x0a, + 0x0d, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x22, 0x30, + 0x0a, 0x0e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x32, 0xf9, 0x02, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x62, 0x6f, 0x78, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x44, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4d, 0x6f, + 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, + 0x6e, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x49, 0x0a, + 0x18, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x72, 0x65, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x61, + 0x74, 0x61, 0x1a, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x12, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x48, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x55, 0x6e, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x44, 0x61, 0x74, 0x61, 0x1a, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0f, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x15, 0x5a, 0x13, + 0x2e, 0x2f, 0x3b, 0x73, 0x79, 0x73, 0x62, 0x6f, 0x78, 0x46, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_sysboxFsProtobuf_proto_rawDescOnce sync.Once + file_sysboxFsProtobuf_proto_rawDescData = file_sysboxFsProtobuf_proto_rawDesc +) + +func file_sysboxFsProtobuf_proto_rawDescGZIP() []byte { + file_sysboxFsProtobuf_proto_rawDescOnce.Do(func() { + file_sysboxFsProtobuf_proto_rawDescData = protoimpl.X.CompressGZIP(file_sysboxFsProtobuf_proto_rawDescData) + }) + return file_sysboxFsProtobuf_proto_rawDescData +} + +var file_sysboxFsProtobuf_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_sysboxFsProtobuf_proto_goTypes = []interface{}{ + (*ContainerData)(nil), // 0: protobuf.ContainerData + (*Response)(nil), // 1: protobuf.Response + (*MountpointReq)(nil), // 2: protobuf.MountpointReq + (*MountpointResp)(nil), // 3: protobuf.MountpointResp + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp +} +var file_sysboxFsProtobuf_proto_depIdxs = []int32{ + 4, // 0: protobuf.ContainerData.Ctime:type_name -> google.protobuf.Timestamp + 2, // 1: protobuf.sysboxStateChannel.GetMountpoint:input_type -> protobuf.MountpointReq + 0, // 2: protobuf.sysboxStateChannel.ContainerPreRegistration:input_type -> protobuf.ContainerData + 0, // 3: protobuf.sysboxStateChannel.ContainerRegistration:input_type -> protobuf.ContainerData + 0, // 4: protobuf.sysboxStateChannel.ContainerUnregistration:input_type -> protobuf.ContainerData + 0, // 5: protobuf.sysboxStateChannel.ContainerUpdate:input_type -> protobuf.ContainerData + 3, // 6: protobuf.sysboxStateChannel.GetMountpoint:output_type -> protobuf.MountpointResp + 1, // 7: protobuf.sysboxStateChannel.ContainerPreRegistration:output_type -> protobuf.Response + 1, // 8: protobuf.sysboxStateChannel.ContainerRegistration:output_type -> protobuf.Response + 1, // 9: protobuf.sysboxStateChannel.ContainerUnregistration:output_type -> protobuf.Response + 1, // 10: protobuf.sysboxStateChannel.ContainerUpdate:output_type -> protobuf.Response + 6, // [6:11] is the sub-list for method output_type + 1, // [1:6] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_sysboxFsProtobuf_proto_init() } +func file_sysboxFsProtobuf_proto_init() { + if File_sysboxFsProtobuf_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_sysboxFsProtobuf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxFsProtobuf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxFsProtobuf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountpointReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxFsProtobuf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountpointResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_sysboxFsProtobuf_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_sysboxFsProtobuf_proto_goTypes, + DependencyIndexes: file_sysboxFsProtobuf_proto_depIdxs, + MessageInfos: file_sysboxFsProtobuf_proto_msgTypes, + }.Build() + File_sysboxFsProtobuf_proto = out.File + file_sysboxFsProtobuf_proto_rawDesc = nil + file_sysboxFsProtobuf_proto_goTypes = nil + file_sysboxFsProtobuf_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// SysboxStateChannelClient is the client API for SysboxStateChannel service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type SysboxStateChannelClient interface { + // Queries sysbox-fs for the FUSE mountpoint + GetMountpoint(ctx context.Context, in *MountpointReq, opts ...grpc.CallOption) (*MountpointResp, error) + // Generates a container-preregistration message + ContainerPreRegistration(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) + // Generates a container-registration message + ContainerRegistration(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) + // Generates a container-unregistration message + ContainerUnregistration(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) + // Generates a container-update message + ContainerUpdate(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) +} + +type sysboxStateChannelClient struct { + cc grpc.ClientConnInterface +} + +func NewSysboxStateChannelClient(cc grpc.ClientConnInterface) SysboxStateChannelClient { + return &sysboxStateChannelClient{cc} +} + +func (c *sysboxStateChannelClient) GetMountpoint(ctx context.Context, in *MountpointReq, opts ...grpc.CallOption) (*MountpointResp, error) { + out := new(MountpointResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxStateChannel/GetMountpoint", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxStateChannelClient) ContainerPreRegistration(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxStateChannel/ContainerPreRegistration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxStateChannelClient) ContainerRegistration(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxStateChannel/ContainerRegistration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxStateChannelClient) ContainerUnregistration(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxStateChannel/ContainerUnregistration", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxStateChannelClient) ContainerUpdate(ctx context.Context, in *ContainerData, opts ...grpc.CallOption) (*Response, error) { + out := new(Response) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxStateChannel/ContainerUpdate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SysboxStateChannelServer is the server API for SysboxStateChannel service. +type SysboxStateChannelServer interface { + // Queries sysbox-fs for the FUSE mountpoint + GetMountpoint(context.Context, *MountpointReq) (*MountpointResp, error) + // Generates a container-preregistration message + ContainerPreRegistration(context.Context, *ContainerData) (*Response, error) + // Generates a container-registration message + ContainerRegistration(context.Context, *ContainerData) (*Response, error) + // Generates a container-unregistration message + ContainerUnregistration(context.Context, *ContainerData) (*Response, error) + // Generates a container-update message + ContainerUpdate(context.Context, *ContainerData) (*Response, error) +} + +// UnimplementedSysboxStateChannelServer can be embedded to have forward compatible implementations. +type UnimplementedSysboxStateChannelServer struct { +} + +func (*UnimplementedSysboxStateChannelServer) GetMountpoint(context.Context, *MountpointReq) (*MountpointResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMountpoint not implemented") +} +func (*UnimplementedSysboxStateChannelServer) ContainerPreRegistration(context.Context, *ContainerData) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerPreRegistration not implemented") +} +func (*UnimplementedSysboxStateChannelServer) ContainerRegistration(context.Context, *ContainerData) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerRegistration not implemented") +} +func (*UnimplementedSysboxStateChannelServer) ContainerUnregistration(context.Context, *ContainerData) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerUnregistration not implemented") +} +func (*UnimplementedSysboxStateChannelServer) ContainerUpdate(context.Context, *ContainerData) (*Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ContainerUpdate not implemented") +} + +func RegisterSysboxStateChannelServer(s *grpc.Server, srv SysboxStateChannelServer) { + s.RegisterService(&_SysboxStateChannel_serviceDesc, srv) +} + +func _SysboxStateChannel_GetMountpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MountpointReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxStateChannelServer).GetMountpoint(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxStateChannel/GetMountpoint", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxStateChannelServer).GetMountpoint(ctx, req.(*MountpointReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxStateChannel_ContainerPreRegistration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerData) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxStateChannelServer).ContainerPreRegistration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxStateChannel/ContainerPreRegistration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxStateChannelServer).ContainerPreRegistration(ctx, req.(*ContainerData)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxStateChannel_ContainerRegistration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerData) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxStateChannelServer).ContainerRegistration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxStateChannel/ContainerRegistration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxStateChannelServer).ContainerRegistration(ctx, req.(*ContainerData)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxStateChannel_ContainerUnregistration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerData) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxStateChannelServer).ContainerUnregistration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxStateChannel/ContainerUnregistration", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxStateChannelServer).ContainerUnregistration(ctx, req.(*ContainerData)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxStateChannel_ContainerUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ContainerData) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxStateChannelServer).ContainerUpdate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxStateChannel/ContainerUpdate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxStateChannelServer).ContainerUpdate(ctx, req.(*ContainerData)) + } + return interceptor(ctx, in, info, handler) +} + +var _SysboxStateChannel_serviceDesc = grpc.ServiceDesc{ + ServiceName: "protobuf.sysboxStateChannel", + HandlerType: (*SysboxStateChannelServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetMountpoint", + Handler: _SysboxStateChannel_GetMountpoint_Handler, + }, + { + MethodName: "ContainerPreRegistration", + Handler: _SysboxStateChannel_ContainerPreRegistration_Handler, + }, + { + MethodName: "ContainerRegistration", + Handler: _SysboxStateChannel_ContainerRegistration_Handler, + }, + { + MethodName: "ContainerUnregistration", + Handler: _SysboxStateChannel_ContainerUnregistration_Handler, + }, + { + MethodName: "ContainerUpdate", + Handler: _SysboxStateChannel_ContainerUpdate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "sysboxFsProtobuf.proto", +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysboxMgrProtobuf.pb.go b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysboxMgrProtobuf.pb.go new file mode 100644 index 000000000..1c0364d2f --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-sysbox/files/sysboxMgrProtobuf.pb.go @@ -0,0 +1,3203 @@ +// +// SysboxMgr Protobuffer Definitions. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v3.21.12 +// source: sysboxMgrProtobuf.proto + +package sysboxMgrProtobuf + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type IDMapping struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerID uint32 `protobuf:"varint,1,opt,name=containerID,proto3" json:"containerID,omitempty"` + HostID uint32 `protobuf:"varint,2,opt,name=hostID,proto3" json:"hostID,omitempty"` + Size uint32 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` +} + +func (x *IDMapping) Reset() { + *x = IDMapping{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IDMapping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IDMapping) ProtoMessage() {} + +func (x *IDMapping) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IDMapping.ProtoReflect.Descriptor instead. +func (*IDMapping) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{0} +} + +func (x *IDMapping) GetContainerID() uint32 { + if x != nil { + return x.ContainerID + } + return 0 +} + +func (x *IDMapping) GetHostID() uint32 { + if x != nil { + return x.HostID + } + return 0 +} + +func (x *IDMapping) GetSize() uint32 { + if x != nil { + return x.Size + } + return 0 +} + +type RegisterReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Rootfs string `protobuf:"bytes,2,opt,name=rootfs,proto3" json:"rootfs,omitempty"` + Userns string `protobuf:"bytes,3,opt,name=userns,proto3" json:"userns,omitempty"` + Netns string `protobuf:"bytes,4,opt,name=netns,proto3" json:"netns,omitempty"` + UidMappings []*IDMapping `protobuf:"bytes,5,rep,name=uidMappings,proto3" json:"uidMappings,omitempty"` + GidMappings []*IDMapping `protobuf:"bytes,6,rep,name=gidMappings,proto3" json:"gidMappings,omitempty"` +} + +func (x *RegisterReq) Reset() { + *x = RegisterReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterReq) ProtoMessage() {} + +func (x *RegisterReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterReq.ProtoReflect.Descriptor instead. +func (*RegisterReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisterReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *RegisterReq) GetRootfs() string { + if x != nil { + return x.Rootfs + } + return "" +} + +func (x *RegisterReq) GetUserns() string { + if x != nil { + return x.Userns + } + return "" +} + +func (x *RegisterReq) GetNetns() string { + if x != nil { + return x.Netns + } + return "" +} + +func (x *RegisterReq) GetUidMappings() []*IDMapping { + if x != nil { + return x.UidMappings + } + return nil +} + +func (x *RegisterReq) GetGidMappings() []*IDMapping { + if x != nil { + return x.GidMappings + } + return nil +} + +type ContainerConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AliasDns bool `protobuf:"varint,1,opt,name=aliasDns,proto3" json:"aliasDns,omitempty"` + ShiftfsOk bool `protobuf:"varint,2,opt,name=ShiftfsOk,proto3" json:"ShiftfsOk,omitempty"` + ShiftfsOnOverlayfsOk bool `protobuf:"varint,3,opt,name=ShiftfsOnOverlayfsOk,proto3" json:"ShiftfsOnOverlayfsOk,omitempty"` + IDMapMountOk bool `protobuf:"varint,4,opt,name=IDMapMountOk,proto3" json:"IDMapMountOk,omitempty"` + OverlayfsOnIDMapMountOk bool `protobuf:"varint,5,opt,name=OverlayfsOnIDMapMountOk,proto3" json:"OverlayfsOnIDMapMountOk,omitempty"` + NoRootfsCloning bool `protobuf:"varint,6,opt,name=noRootfsCloning,proto3" json:"noRootfsCloning,omitempty"` + IgnoreSysfsChown bool `protobuf:"varint,7,opt,name=ignoreSysfsChown,proto3" json:"ignoreSysfsChown,omitempty"` + AllowTrustedXattr bool `protobuf:"varint,8,opt,name=allowTrustedXattr,proto3" json:"allowTrustedXattr,omitempty"` + HonorCaps bool `protobuf:"varint,9,opt,name=honorCaps,proto3" json:"honorCaps,omitempty"` + SyscontMode bool `protobuf:"varint,10,opt,name=syscontMode,proto3" json:"syscontMode,omitempty"` + Userns string `protobuf:"bytes,11,opt,name=userns,proto3" json:"userns,omitempty"` + UidMappings []*IDMapping `protobuf:"bytes,12,rep,name=uidMappings,proto3" json:"uidMappings,omitempty"` + GidMappings []*IDMapping `protobuf:"bytes,13,rep,name=gidMappings,proto3" json:"gidMappings,omitempty"` + FsuidMapFailOnErr bool `protobuf:"varint,14,opt,name=fsuidMapFailOnErr,proto3" json:"fsuidMapFailOnErr,omitempty"` + RootfsUidShiftType uint32 `protobuf:"varint,15,opt,name=rootfsUidShiftType,proto3" json:"rootfsUidShiftType,omitempty"` + NoShiftfsOnFuse bool `protobuf:"varint,16,opt,name=noShiftfsOnFuse,proto3" json:"noShiftfsOnFuse,omitempty"` + RelaxedReadOnly bool `protobuf:"varint,17,opt,name=relaxedReadOnly,proto3" json:"relaxedReadOnly,omitempty"` +} + +func (x *ContainerConfig) Reset() { + *x = ContainerConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ContainerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ContainerConfig) ProtoMessage() {} + +func (x *ContainerConfig) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ContainerConfig.ProtoReflect.Descriptor instead. +func (*ContainerConfig) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{2} +} + +func (x *ContainerConfig) GetAliasDns() bool { + if x != nil { + return x.AliasDns + } + return false +} + +func (x *ContainerConfig) GetShiftfsOk() bool { + if x != nil { + return x.ShiftfsOk + } + return false +} + +func (x *ContainerConfig) GetShiftfsOnOverlayfsOk() bool { + if x != nil { + return x.ShiftfsOnOverlayfsOk + } + return false +} + +func (x *ContainerConfig) GetIDMapMountOk() bool { + if x != nil { + return x.IDMapMountOk + } + return false +} + +func (x *ContainerConfig) GetOverlayfsOnIDMapMountOk() bool { + if x != nil { + return x.OverlayfsOnIDMapMountOk + } + return false +} + +func (x *ContainerConfig) GetNoRootfsCloning() bool { + if x != nil { + return x.NoRootfsCloning + } + return false +} + +func (x *ContainerConfig) GetIgnoreSysfsChown() bool { + if x != nil { + return x.IgnoreSysfsChown + } + return false +} + +func (x *ContainerConfig) GetAllowTrustedXattr() bool { + if x != nil { + return x.AllowTrustedXattr + } + return false +} + +func (x *ContainerConfig) GetHonorCaps() bool { + if x != nil { + return x.HonorCaps + } + return false +} + +func (x *ContainerConfig) GetSyscontMode() bool { + if x != nil { + return x.SyscontMode + } + return false +} + +func (x *ContainerConfig) GetUserns() string { + if x != nil { + return x.Userns + } + return "" +} + +func (x *ContainerConfig) GetUidMappings() []*IDMapping { + if x != nil { + return x.UidMappings + } + return nil +} + +func (x *ContainerConfig) GetGidMappings() []*IDMapping { + if x != nil { + return x.GidMappings + } + return nil +} + +func (x *ContainerConfig) GetFsuidMapFailOnErr() bool { + if x != nil { + return x.FsuidMapFailOnErr + } + return false +} + +func (x *ContainerConfig) GetRootfsUidShiftType() uint32 { + if x != nil { + return x.RootfsUidShiftType + } + return 0 +} + +func (x *ContainerConfig) GetNoShiftfsOnFuse() bool { + if x != nil { + return x.NoShiftfsOnFuse + } + return false +} + +func (x *ContainerConfig) GetRelaxedReadOnly() bool { + if x != nil { + return x.RelaxedReadOnly + } + return false +} + +type RegisterResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerConfig *ContainerConfig `protobuf:"bytes,1,opt,name=containerConfig,proto3" json:"containerConfig,omitempty"` +} + +func (x *RegisterResp) Reset() { + *x = RegisterResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterResp) ProtoMessage() {} + +func (x *RegisterResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterResp.ProtoReflect.Descriptor instead. +func (*RegisterResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{3} +} + +func (x *RegisterResp) GetContainerConfig() *ContainerConfig { + if x != nil { + return x.ContainerConfig + } + return nil +} + +type UpdateReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Userns string `protobuf:"bytes,2,opt,name=userns,proto3" json:"userns,omitempty"` + Netns string `protobuf:"bytes,3,opt,name=netns,proto3" json:"netns,omitempty"` + UidMappings []*IDMapping `protobuf:"bytes,4,rep,name=uidMappings,proto3" json:"uidMappings,omitempty"` + GidMappings []*IDMapping `protobuf:"bytes,5,rep,name=gidMappings,proto3" json:"gidMappings,omitempty"` + RootfsUidShiftType uint32 `protobuf:"varint,6,opt,name=rootfsUidShiftType,proto3" json:"rootfsUidShiftType,omitempty"` +} + +func (x *UpdateReq) Reset() { + *x = UpdateReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateReq) ProtoMessage() {} + +func (x *UpdateReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateReq.ProtoReflect.Descriptor instead. +func (*UpdateReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{4} +} + +func (x *UpdateReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateReq) GetUserns() string { + if x != nil { + return x.Userns + } + return "" +} + +func (x *UpdateReq) GetNetns() string { + if x != nil { + return x.Netns + } + return "" +} + +func (x *UpdateReq) GetUidMappings() []*IDMapping { + if x != nil { + return x.UidMappings + } + return nil +} + +func (x *UpdateReq) GetGidMappings() []*IDMapping { + if x != nil { + return x.GidMappings + } + return nil +} + +func (x *UpdateReq) GetRootfsUidShiftType() uint32 { + if x != nil { + return x.RootfsUidShiftType + } + return 0 +} + +type UpdateResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpdateResp) Reset() { + *x = UpdateResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResp) ProtoMessage() {} + +func (x *UpdateResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateResp.ProtoReflect.Descriptor instead. +func (*UpdateResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{5} +} + +type UnregisterReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *UnregisterReq) Reset() { + *x = UnregisterReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnregisterReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnregisterReq) ProtoMessage() {} + +func (x *UnregisterReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnregisterReq.ProtoReflect.Descriptor instead. +func (*UnregisterReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{6} +} + +func (x *UnregisterReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type UnregisterResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UnregisterResp) Reset() { + *x = UnregisterResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnregisterResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnregisterResp) ProtoMessage() {} + +func (x *UnregisterResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnregisterResp.ProtoReflect.Descriptor instead. +func (*UnregisterResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{7} +} + +type SubidAllocReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Size uint64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` +} + +func (x *SubidAllocReq) Reset() { + *x = SubidAllocReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubidAllocReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubidAllocReq) ProtoMessage() {} + +func (x *SubidAllocReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubidAllocReq.ProtoReflect.Descriptor instead. +func (*SubidAllocReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{8} +} + +func (x *SubidAllocReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *SubidAllocReq) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +type SubidAllocResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid uint32 `protobuf:"varint,1,opt,name=uid,proto3" json:"uid,omitempty"` + Gid uint32 `protobuf:"varint,2,opt,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *SubidAllocResp) Reset() { + *x = SubidAllocResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubidAllocResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubidAllocResp) ProtoMessage() {} + +func (x *SubidAllocResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubidAllocResp.ProtoReflect.Descriptor instead. +func (*SubidAllocResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{9} +} + +func (x *SubidAllocResp) GetUid() uint32 { + if x != nil { + return x.Uid + } + return 0 +} + +func (x *SubidAllocResp) GetGid() uint32 { + if x != nil { + return x.Gid + } + return 0 +} + +type MountPrepInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + Exclusive bool `protobuf:"varint,2,opt,name=exclusive,proto3" json:"exclusive,omitempty"` +} + +func (x *MountPrepInfo) Reset() { + *x = MountPrepInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountPrepInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountPrepInfo) ProtoMessage() {} + +func (x *MountPrepInfo) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountPrepInfo.ProtoReflect.Descriptor instead. +func (*MountPrepInfo) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{10} +} + +func (x *MountPrepInfo) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *MountPrepInfo) GetExclusive() bool { + if x != nil { + return x.Exclusive + } + return false +} + +type MountPrepReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Uid uint32 `protobuf:"varint,2,opt,name=uid,proto3" json:"uid,omitempty"` + Gid uint32 `protobuf:"varint,3,opt,name=gid,proto3" json:"gid,omitempty"` + PrepList []*MountPrepInfo `protobuf:"bytes,4,rep,name=prepList,proto3" json:"prepList,omitempty"` +} + +func (x *MountPrepReq) Reset() { + *x = MountPrepReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountPrepReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountPrepReq) ProtoMessage() {} + +func (x *MountPrepReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountPrepReq.ProtoReflect.Descriptor instead. +func (*MountPrepReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{11} +} + +func (x *MountPrepReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *MountPrepReq) GetUid() uint32 { + if x != nil { + return x.Uid + } + return 0 +} + +func (x *MountPrepReq) GetGid() uint32 { + if x != nil { + return x.Gid + } + return 0 +} + +func (x *MountPrepReq) GetPrepList() []*MountPrepInfo { + if x != nil { + return x.PrepList + } + return nil +} + +type MountPrepResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *MountPrepResp) Reset() { + *x = MountPrepResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountPrepResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountPrepResp) ProtoMessage() {} + +func (x *MountPrepResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountPrepResp.ProtoReflect.Descriptor instead. +func (*MountPrepResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{12} +} + +type MountReqInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kind uint32 `protobuf:"varint,1,opt,name=kind,proto3" json:"kind,omitempty"` + Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"` +} + +func (x *MountReqInfo) Reset() { + *x = MountReqInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountReqInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountReqInfo) ProtoMessage() {} + +func (x *MountReqInfo) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountReqInfo.ProtoReflect.Descriptor instead. +func (*MountReqInfo) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{13} +} + +func (x *MountReqInfo) GetKind() uint32 { + if x != nil { + return x.Kind + } + return 0 +} + +func (x *MountReqInfo) GetDest() string { + if x != nil { + return x.Dest + } + return "" +} + +type MountReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + RootfsUidShiftType uint32 `protobuf:"varint,2,opt,name=rootfsUidShiftType,proto3" json:"rootfsUidShiftType,omitempty"` + ReqList []*MountReqInfo `protobuf:"bytes,3,rep,name=reqList,proto3" json:"reqList,omitempty"` +} + +func (x *MountReq) Reset() { + *x = MountReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountReq) ProtoMessage() {} + +func (x *MountReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountReq.ProtoReflect.Descriptor instead. +func (*MountReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{14} +} + +func (x *MountReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *MountReq) GetRootfsUidShiftType() uint32 { + if x != nil { + return x.RootfsUidShiftType + } + return 0 +} + +func (x *MountReq) GetReqList() []*MountReqInfo { + if x != nil { + return x.ReqList + } + return nil +} + +type Mount struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + Opt []string `protobuf:"bytes,4,rep,name=opt,proto3" json:"opt,omitempty"` +} + +func (x *Mount) Reset() { + *x = Mount{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Mount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Mount) ProtoMessage() {} + +func (x *Mount) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Mount.ProtoReflect.Descriptor instead. +func (*Mount) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{15} +} + +func (x *Mount) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *Mount) GetDest() string { + if x != nil { + return x.Dest + } + return "" +} + +func (x *Mount) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Mount) GetOpt() []string { + if x != nil { + return x.Opt + } + return nil +} + +type MountResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mounts []*Mount `protobuf:"bytes,1,rep,name=mounts,proto3" json:"mounts,omitempty"` +} + +func (x *MountResp) Reset() { + *x = MountResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MountResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountResp) ProtoMessage() {} + +func (x *MountResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MountResp.ProtoReflect.Descriptor instead. +func (*MountResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{16} +} + +func (x *MountResp) GetMounts() []*Mount { + if x != nil { + return x.Mounts + } + return nil +} + +type ShiftfsMark struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + Readonly bool `protobuf:"varint,2,opt,name=readonly,proto3" json:"readonly,omitempty"` +} + +func (x *ShiftfsMark) Reset() { + *x = ShiftfsMark{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShiftfsMark) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShiftfsMark) ProtoMessage() {} + +func (x *ShiftfsMark) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShiftfsMark.ProtoReflect.Descriptor instead. +func (*ShiftfsMark) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{17} +} + +func (x *ShiftfsMark) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *ShiftfsMark) GetReadonly() bool { + if x != nil { + return x.Readonly + } + return false +} + +type ShiftfsMarkReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ShiftfsMarks []*ShiftfsMark `protobuf:"bytes,2,rep,name=shiftfsMarks,proto3" json:"shiftfsMarks,omitempty"` +} + +func (x *ShiftfsMarkReq) Reset() { + *x = ShiftfsMarkReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShiftfsMarkReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShiftfsMarkReq) ProtoMessage() {} + +func (x *ShiftfsMarkReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShiftfsMarkReq.ProtoReflect.Descriptor instead. +func (*ShiftfsMarkReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{18} +} + +func (x *ShiftfsMarkReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ShiftfsMarkReq) GetShiftfsMarks() []*ShiftfsMark { + if x != nil { + return x.ShiftfsMarks + } + return nil +} + +type ShiftfsMarkResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ShiftfsMarks []*ShiftfsMark `protobuf:"bytes,1,rep,name=shiftfsMarks,proto3" json:"shiftfsMarks,omitempty"` +} + +func (x *ShiftfsMarkResp) Reset() { + *x = ShiftfsMarkResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShiftfsMarkResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShiftfsMarkResp) ProtoMessage() {} + +func (x *ShiftfsMarkResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShiftfsMarkResp.ProtoReflect.Descriptor instead. +func (*ShiftfsMarkResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{19} +} + +func (x *ShiftfsMarkResp) GetShiftfsMarks() []*ShiftfsMark { + if x != nil { + return x.ShiftfsMarks + } + return nil +} + +type FsStateReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Rootfs string `protobuf:"bytes,2,opt,name=rootfs,proto3" json:"rootfs,omitempty"` +} + +func (x *FsStateReq) Reset() { + *x = FsStateReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FsStateReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FsStateReq) ProtoMessage() {} + +func (x *FsStateReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FsStateReq.ProtoReflect.Descriptor instead. +func (*FsStateReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{20} +} + +func (x *FsStateReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *FsStateReq) GetRootfs() string { + if x != nil { + return x.Rootfs + } + return "" +} + +type FsEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Kind uint32 `protobuf:"varint,1,opt,name=kind,proto3" json:"kind,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Mode uint32 `protobuf:"varint,3,opt,name=mode,proto3" json:"mode,omitempty"` + Dst string `protobuf:"bytes,4,opt,name=dst,proto3" json:"dst,omitempty"` +} + +func (x *FsEntry) Reset() { + *x = FsEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FsEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FsEntry) ProtoMessage() {} + +func (x *FsEntry) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FsEntry.ProtoReflect.Descriptor instead. +func (*FsEntry) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{21} +} + +func (x *FsEntry) GetKind() uint32 { + if x != nil { + return x.Kind + } + return 0 +} + +func (x *FsEntry) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *FsEntry) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +func (x *FsEntry) GetDst() string { + if x != nil { + return x.Dst + } + return "" +} + +type FsStateResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FsEntries []*FsEntry `protobuf:"bytes,1,rep,name=fsEntries,proto3" json:"fsEntries,omitempty"` +} + +func (x *FsStateResp) Reset() { + *x = FsStateResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FsStateResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FsStateResp) ProtoMessage() {} + +func (x *FsStateResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FsStateResp.ProtoReflect.Descriptor instead. +func (*FsStateResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{22} +} + +func (x *FsStateResp) GetFsEntries() []*FsEntry { + if x != nil { + return x.FsEntries + } + return nil +} + +type PauseReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *PauseReq) Reset() { + *x = PauseReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PauseReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PauseReq) ProtoMessage() {} + +func (x *PauseReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PauseReq.ProtoReflect.Descriptor instead. +func (*PauseReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{23} +} + +func (x *PauseReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type PauseResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PauseResp) Reset() { + *x = PauseResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PauseResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PauseResp) ProtoMessage() {} + +func (x *PauseResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PauseResp.ProtoReflect.Descriptor instead. +func (*PauseResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{24} +} + +type ResumeReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ResumeReq) Reset() { + *x = ResumeReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResumeReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResumeReq) ProtoMessage() {} + +func (x *ResumeReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResumeReq.ProtoReflect.Descriptor instead. +func (*ResumeReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{25} +} + +func (x *ResumeReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ResumeResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ResumeResp) Reset() { + *x = ResumeResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResumeResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResumeResp) ProtoMessage() {} + +func (x *ResumeResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResumeResp.ProtoReflect.Descriptor instead. +func (*ResumeResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{26} +} + +type CloneRootfsReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *CloneRootfsReq) Reset() { + *x = CloneRootfsReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CloneRootfsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloneRootfsReq) ProtoMessage() {} + +func (x *CloneRootfsReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CloneRootfsReq.ProtoReflect.Descriptor instead. +func (*CloneRootfsReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{27} +} + +func (x *CloneRootfsReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type CloneRootfsResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Rootfs string `protobuf:"bytes,1,opt,name=rootfs,proto3" json:"rootfs,omitempty"` +} + +func (x *CloneRootfsResp) Reset() { + *x = CloneRootfsResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CloneRootfsResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloneRootfsResp) ProtoMessage() {} + +func (x *CloneRootfsResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CloneRootfsResp.ProtoReflect.Descriptor instead. +func (*CloneRootfsResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{28} +} + +func (x *CloneRootfsResp) GetRootfs() string { + if x != nil { + return x.Rootfs + } + return "" +} + +type ChownClonedRootfsReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + UidOffset int32 `protobuf:"varint,2,opt,name=uidOffset,proto3" json:"uidOffset,omitempty"` + GidOffset int32 `protobuf:"varint,3,opt,name=gidOffset,proto3" json:"gidOffset,omitempty"` +} + +func (x *ChownClonedRootfsReq) Reset() { + *x = ChownClonedRootfsReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChownClonedRootfsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChownClonedRootfsReq) ProtoMessage() {} + +func (x *ChownClonedRootfsReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChownClonedRootfsReq.ProtoReflect.Descriptor instead. +func (*ChownClonedRootfsReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{29} +} + +func (x *ChownClonedRootfsReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ChownClonedRootfsReq) GetUidOffset() int32 { + if x != nil { + return x.UidOffset + } + return 0 +} + +func (x *ChownClonedRootfsReq) GetGidOffset() int32 { + if x != nil { + return x.GidOffset + } + return 0 +} + +type ChownClonedRootfsResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ChownClonedRootfsResp) Reset() { + *x = ChownClonedRootfsResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChownClonedRootfsResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChownClonedRootfsResp) ProtoMessage() {} + +func (x *ChownClonedRootfsResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChownClonedRootfsResp.ProtoReflect.Descriptor instead. +func (*ChownClonedRootfsResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{30} +} + +type RevertClonedRootfsChownReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *RevertClonedRootfsChownReq) Reset() { + *x = RevertClonedRootfsChownReq{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RevertClonedRootfsChownReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RevertClonedRootfsChownReq) ProtoMessage() {} + +func (x *RevertClonedRootfsChownReq) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RevertClonedRootfsChownReq.ProtoReflect.Descriptor instead. +func (*RevertClonedRootfsChownReq) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{31} +} + +func (x *RevertClonedRootfsChownReq) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type RevertClonedRootfsChownResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RevertClonedRootfsChownResp) Reset() { + *x = RevertClonedRootfsChownResp{} + if protoimpl.UnsafeEnabled { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RevertClonedRootfsChownResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RevertClonedRootfsChownResp) ProtoMessage() {} + +func (x *RevertClonedRootfsChownResp) ProtoReflect() protoreflect.Message { + mi := &file_sysboxMgrProtobuf_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RevertClonedRootfsChownResp.ProtoReflect.Descriptor instead. +func (*RevertClonedRootfsChownResp) Descriptor() ([]byte, []int) { + return file_sysboxMgrProtobuf_proto_rawDescGZIP(), []int{32} +} + +var File_sysboxMgrProtobuf_proto protoreflect.FileDescriptor + +var file_sysboxMgrProtobuf_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x73, 0x79, 0x73, 0x62, 0x6f, 0x78, 0x4d, 0x67, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x22, 0x59, 0x0a, 0x09, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x68, 0x6f, 0x73, 0x74, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xd1, + 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, + 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, + 0x65, 0x74, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x75, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, + 0x75, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x67, + 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x44, 0x4d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x67, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, + 0x67, 0x73, 0x22, 0xd9, 0x05, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x44, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x44, + 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4f, 0x6b, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4f, 0x6b, + 0x12, 0x32, 0x0a, 0x14, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4f, 0x6e, 0x4f, 0x76, 0x65, + 0x72, 0x6c, 0x61, 0x79, 0x66, 0x73, 0x4f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, + 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, + 0x66, 0x73, 0x4f, 0x6b, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x4d, 0x6f, 0x75, + 0x6e, 0x74, 0x4f, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x49, 0x44, 0x4d, 0x61, + 0x70, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6b, 0x12, 0x38, 0x0a, 0x17, 0x4f, 0x76, 0x65, 0x72, + 0x6c, 0x61, 0x79, 0x66, 0x73, 0x4f, 0x6e, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x4d, 0x6f, 0x75, 0x6e, + 0x74, 0x4f, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x4f, 0x76, 0x65, 0x72, 0x6c, + 0x61, 0x79, 0x66, 0x73, 0x4f, 0x6e, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x4f, 0x6b, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x43, 0x6c, + 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x52, + 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x43, 0x6c, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x2a, 0x0a, 0x10, + 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x53, 0x79, 0x73, 0x66, 0x73, 0x43, 0x68, 0x6f, 0x77, 0x6e, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x53, 0x79, + 0x73, 0x66, 0x73, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x12, 0x2c, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x58, 0x61, 0x74, 0x74, 0x72, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, + 0x64, 0x58, 0x61, 0x74, 0x74, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x68, 0x6f, 0x6e, 0x6f, 0x72, 0x43, + 0x61, 0x70, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x6f, 0x6e, 0x6f, 0x72, + 0x43, 0x61, 0x70, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x4d, + 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x63, 0x6f, + 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x35, + 0x0a, 0x0b, 0x75, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0c, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, + 0x44, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x75, 0x69, 0x64, 0x4d, 0x61, 0x70, + 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x67, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, + 0x0b, 0x67, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x11, + 0x66, 0x73, 0x75, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x46, 0x61, 0x69, 0x6c, 0x4f, 0x6e, 0x45, 0x72, + 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x66, 0x73, 0x75, 0x69, 0x64, 0x4d, 0x61, + 0x70, 0x46, 0x61, 0x69, 0x6c, 0x4f, 0x6e, 0x45, 0x72, 0x72, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x6f, + 0x6f, 0x74, 0x66, 0x73, 0x55, 0x69, 0x64, 0x53, 0x68, 0x69, 0x66, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x55, 0x69, + 0x64, 0x53, 0x68, 0x69, 0x66, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, + 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4f, 0x6e, 0x46, 0x75, 0x73, 0x65, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6e, 0x6f, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4f, 0x6e, + 0x46, 0x75, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x52, + 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, + 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x53, + 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x43, + 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0xe7, 0x01, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74, + 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x12, + 0x35, 0x0a, 0x0b, 0x75, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x49, 0x44, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x75, 0x69, 0x64, 0x4d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x67, 0x69, 0x64, 0x4d, 0x61, 0x70, + 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x44, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x52, 0x0b, 0x67, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2e, 0x0a, + 0x12, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x55, 0x69, 0x64, 0x53, 0x68, 0x69, 0x66, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x72, 0x6f, 0x6f, 0x74, 0x66, + 0x73, 0x55, 0x69, 0x64, 0x53, 0x68, 0x69, 0x66, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x0c, 0x0a, + 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x1f, 0x0a, 0x0d, 0x55, + 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, + 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x33, + 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x69, 0x64, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x52, 0x65, 0x71, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x22, 0x34, 0x0a, 0x0e, 0x53, 0x75, 0x62, 0x69, 0x64, 0x41, 0x6c, 0x6c, 0x6f, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x45, 0x0a, 0x0d, 0x4d, 0x6f, 0x75, + 0x6e, 0x74, 0x50, 0x72, 0x65, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, + 0x22, 0x77, 0x0a, 0x0c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x70, 0x52, 0x65, 0x71, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x75, + 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x03, 0x67, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x70, 0x4c, 0x69, 0x73, 0x74, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x08, 0x70, 0x72, 0x65, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x4d, 0x6f, 0x75, + 0x6e, 0x74, 0x50, 0x72, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x22, 0x36, 0x0a, 0x0c, 0x4d, 0x6f, + 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, + 0x73, 0x74, 0x22, 0x7c, 0x0a, 0x08, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, + 0x0a, 0x12, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x55, 0x69, 0x64, 0x53, 0x68, 0x69, 0x66, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x72, 0x6f, 0x6f, 0x74, + 0x66, 0x73, 0x55, 0x69, 0x64, 0x53, 0x68, 0x69, 0x66, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, + 0x0a, 0x07, 0x72, 0x65, 0x71, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x72, 0x65, 0x71, 0x4c, 0x69, 0x73, 0x74, + 0x22, 0x59, 0x0a, 0x05, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x70, 0x74, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x22, 0x34, 0x0a, 0x09, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x27, 0x0a, 0x06, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x06, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x22, 0x41, 0x0a, 0x0b, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x61, 0x64, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, + 0x6f, 0x6e, 0x6c, 0x79, 0x22, 0x5b, 0x0a, 0x0e, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, + 0x61, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x66, + 0x73, 0x4d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, + 0x61, 0x72, 0x6b, 0x52, 0x0c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, + 0x73, 0x22, 0x4c, 0x0a, 0x0f, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x0c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, + 0x61, 0x72, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, + 0x6b, 0x52, 0x0c, 0x73, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, 0x73, 0x22, + 0x34, 0x0a, 0x0a, 0x46, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x22, 0x57, 0x0a, 0x07, 0x46, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, + 0x64, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x73, 0x74, 0x22, 0x3e, + 0x0a, 0x0b, 0x46, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2f, 0x0a, + 0x09, 0x66, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x09, 0x66, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x1a, + 0x0a, 0x08, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0b, 0x0a, 0x09, 0x50, 0x61, + 0x75, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x1b, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x75, 0x6d, + 0x65, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x0c, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x22, 0x20, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x66, + 0x73, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x29, 0x0a, 0x0f, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x52, 0x6f, 0x6f, + 0x74, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x74, 0x66, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x22, + 0x62, 0x0a, 0x14, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, + 0x6f, 0x74, 0x66, 0x73, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x69, 0x64, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x75, 0x69, 0x64, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x69, 0x64, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x67, 0x69, 0x64, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x6e, + 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x2c, 0x0a, 0x1a, + 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, + 0x66, 0x73, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, + 0x76, 0x65, 0x72, 0x74, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, + 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x32, 0x86, 0x07, 0x0a, 0x15, 0x73, 0x79, + 0x73, 0x62, 0x6f, 0x78, 0x4d, 0x67, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x3b, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, + 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, + 0x12, 0x35, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, + 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x55, 0x6e, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x53, 0x75, + 0x62, 0x69, 0x64, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x75, 0x62, 0x69, 0x64, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x52, 0x65, + 0x71, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x75, 0x62, + 0x69, 0x64, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x3f, 0x0a, + 0x0a, 0x50, 0x72, 0x65, 0x70, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x70, + 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x36, + 0x0a, 0x09, 0x52, 0x65, 0x71, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, + 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x53, 0x68, 0x69, + 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x68, 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, 0x52, + 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x68, + 0x69, 0x66, 0x74, 0x66, 0x73, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, + 0x3b, 0x0a, 0x0a, 0x52, 0x65, 0x71, 0x46, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, + 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x32, 0x0a, 0x05, + 0x50, 0x61, 0x75, 0x73, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, + 0x12, 0x35, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x1a, + 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x43, 0x6c, + 0x6f, 0x6e, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, + 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, + 0x6c, 0x6f, 0x6e, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, + 0x12, 0x56, 0x0a, 0x11, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, + 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, + 0x66, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, + 0x66, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x17, 0x52, 0x65, 0x76, 0x65, + 0x72, 0x74, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x43, 0x68, + 0x6f, 0x77, 0x6e, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, + 0x65, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x66, + 0x73, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6c, 0x6f, 0x6e, 0x65, + 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x22, 0x00, 0x42, 0x16, 0x5a, 0x14, 0x2e, 0x2f, 0x3b, 0x73, 0x79, 0x73, 0x62, 0x6f, 0x78, 0x4d, + 0x67, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_sysboxMgrProtobuf_proto_rawDescOnce sync.Once + file_sysboxMgrProtobuf_proto_rawDescData = file_sysboxMgrProtobuf_proto_rawDesc +) + +func file_sysboxMgrProtobuf_proto_rawDescGZIP() []byte { + file_sysboxMgrProtobuf_proto_rawDescOnce.Do(func() { + file_sysboxMgrProtobuf_proto_rawDescData = protoimpl.X.CompressGZIP(file_sysboxMgrProtobuf_proto_rawDescData) + }) + return file_sysboxMgrProtobuf_proto_rawDescData +} + +var file_sysboxMgrProtobuf_proto_msgTypes = make([]protoimpl.MessageInfo, 33) +var file_sysboxMgrProtobuf_proto_goTypes = []interface{}{ + (*IDMapping)(nil), // 0: protobuf.IDMapping + (*RegisterReq)(nil), // 1: protobuf.RegisterReq + (*ContainerConfig)(nil), // 2: protobuf.ContainerConfig + (*RegisterResp)(nil), // 3: protobuf.RegisterResp + (*UpdateReq)(nil), // 4: protobuf.UpdateReq + (*UpdateResp)(nil), // 5: protobuf.UpdateResp + (*UnregisterReq)(nil), // 6: protobuf.UnregisterReq + (*UnregisterResp)(nil), // 7: protobuf.UnregisterResp + (*SubidAllocReq)(nil), // 8: protobuf.SubidAllocReq + (*SubidAllocResp)(nil), // 9: protobuf.SubidAllocResp + (*MountPrepInfo)(nil), // 10: protobuf.MountPrepInfo + (*MountPrepReq)(nil), // 11: protobuf.MountPrepReq + (*MountPrepResp)(nil), // 12: protobuf.MountPrepResp + (*MountReqInfo)(nil), // 13: protobuf.MountReqInfo + (*MountReq)(nil), // 14: protobuf.MountReq + (*Mount)(nil), // 15: protobuf.Mount + (*MountResp)(nil), // 16: protobuf.MountResp + (*ShiftfsMark)(nil), // 17: protobuf.ShiftfsMark + (*ShiftfsMarkReq)(nil), // 18: protobuf.ShiftfsMarkReq + (*ShiftfsMarkResp)(nil), // 19: protobuf.ShiftfsMarkResp + (*FsStateReq)(nil), // 20: protobuf.FsStateReq + (*FsEntry)(nil), // 21: protobuf.FsEntry + (*FsStateResp)(nil), // 22: protobuf.FsStateResp + (*PauseReq)(nil), // 23: protobuf.PauseReq + (*PauseResp)(nil), // 24: protobuf.PauseResp + (*ResumeReq)(nil), // 25: protobuf.ResumeReq + (*ResumeResp)(nil), // 26: protobuf.ResumeResp + (*CloneRootfsReq)(nil), // 27: protobuf.CloneRootfsReq + (*CloneRootfsResp)(nil), // 28: protobuf.CloneRootfsResp + (*ChownClonedRootfsReq)(nil), // 29: protobuf.ChownClonedRootfsReq + (*ChownClonedRootfsResp)(nil), // 30: protobuf.ChownClonedRootfsResp + (*RevertClonedRootfsChownReq)(nil), // 31: protobuf.RevertClonedRootfsChownReq + (*RevertClonedRootfsChownResp)(nil), // 32: protobuf.RevertClonedRootfsChownResp +} +var file_sysboxMgrProtobuf_proto_depIdxs = []int32{ + 0, // 0: protobuf.RegisterReq.uidMappings:type_name -> protobuf.IDMapping + 0, // 1: protobuf.RegisterReq.gidMappings:type_name -> protobuf.IDMapping + 0, // 2: protobuf.ContainerConfig.uidMappings:type_name -> protobuf.IDMapping + 0, // 3: protobuf.ContainerConfig.gidMappings:type_name -> protobuf.IDMapping + 2, // 4: protobuf.RegisterResp.containerConfig:type_name -> protobuf.ContainerConfig + 0, // 5: protobuf.UpdateReq.uidMappings:type_name -> protobuf.IDMapping + 0, // 6: protobuf.UpdateReq.gidMappings:type_name -> protobuf.IDMapping + 10, // 7: protobuf.MountPrepReq.prepList:type_name -> protobuf.MountPrepInfo + 13, // 8: protobuf.MountReq.reqList:type_name -> protobuf.MountReqInfo + 15, // 9: protobuf.MountResp.mounts:type_name -> protobuf.Mount + 17, // 10: protobuf.ShiftfsMarkReq.shiftfsMarks:type_name -> protobuf.ShiftfsMark + 17, // 11: protobuf.ShiftfsMarkResp.shiftfsMarks:type_name -> protobuf.ShiftfsMark + 21, // 12: protobuf.FsStateResp.fsEntries:type_name -> protobuf.FsEntry + 1, // 13: protobuf.sysboxMgrStateChannel.Register:input_type -> protobuf.RegisterReq + 4, // 14: protobuf.sysboxMgrStateChannel.Update:input_type -> protobuf.UpdateReq + 6, // 15: protobuf.sysboxMgrStateChannel.Unregister:input_type -> protobuf.UnregisterReq + 8, // 16: protobuf.sysboxMgrStateChannel.SubidAlloc:input_type -> protobuf.SubidAllocReq + 11, // 17: protobuf.sysboxMgrStateChannel.PrepMounts:input_type -> protobuf.MountPrepReq + 14, // 18: protobuf.sysboxMgrStateChannel.ReqMounts:input_type -> protobuf.MountReq + 18, // 19: protobuf.sysboxMgrStateChannel.ReqShiftfsMark:input_type -> protobuf.ShiftfsMarkReq + 20, // 20: protobuf.sysboxMgrStateChannel.ReqFsState:input_type -> protobuf.FsStateReq + 23, // 21: protobuf.sysboxMgrStateChannel.Pause:input_type -> protobuf.PauseReq + 25, // 22: protobuf.sysboxMgrStateChannel.Resume:input_type -> protobuf.ResumeReq + 27, // 23: protobuf.sysboxMgrStateChannel.ReqCloneRootfs:input_type -> protobuf.CloneRootfsReq + 29, // 24: protobuf.sysboxMgrStateChannel.ChownClonedRootfs:input_type -> protobuf.ChownClonedRootfsReq + 31, // 25: protobuf.sysboxMgrStateChannel.RevertClonedRootfsChown:input_type -> protobuf.RevertClonedRootfsChownReq + 3, // 26: protobuf.sysboxMgrStateChannel.Register:output_type -> protobuf.RegisterResp + 5, // 27: protobuf.sysboxMgrStateChannel.Update:output_type -> protobuf.UpdateResp + 7, // 28: protobuf.sysboxMgrStateChannel.Unregister:output_type -> protobuf.UnregisterResp + 9, // 29: protobuf.sysboxMgrStateChannel.SubidAlloc:output_type -> protobuf.SubidAllocResp + 12, // 30: protobuf.sysboxMgrStateChannel.PrepMounts:output_type -> protobuf.MountPrepResp + 16, // 31: protobuf.sysboxMgrStateChannel.ReqMounts:output_type -> protobuf.MountResp + 19, // 32: protobuf.sysboxMgrStateChannel.ReqShiftfsMark:output_type -> protobuf.ShiftfsMarkResp + 22, // 33: protobuf.sysboxMgrStateChannel.ReqFsState:output_type -> protobuf.FsStateResp + 24, // 34: protobuf.sysboxMgrStateChannel.Pause:output_type -> protobuf.PauseResp + 26, // 35: protobuf.sysboxMgrStateChannel.Resume:output_type -> protobuf.ResumeResp + 28, // 36: protobuf.sysboxMgrStateChannel.ReqCloneRootfs:output_type -> protobuf.CloneRootfsResp + 30, // 37: protobuf.sysboxMgrStateChannel.ChownClonedRootfs:output_type -> protobuf.ChownClonedRootfsResp + 32, // 38: protobuf.sysboxMgrStateChannel.RevertClonedRootfsChown:output_type -> protobuf.RevertClonedRootfsChownResp + 26, // [26:39] is the sub-list for method output_type + 13, // [13:26] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_sysboxMgrProtobuf_proto_init() } +func file_sysboxMgrProtobuf_proto_init() { + if File_sysboxMgrProtobuf_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_sysboxMgrProtobuf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IDMapping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ContainerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnregisterReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnregisterResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubidAllocReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubidAllocResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountPrepInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountPrepReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountPrepResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountReqInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Mount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MountResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShiftfsMark); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShiftfsMarkReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShiftfsMarkResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FsStateReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FsEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FsStateResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PauseReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PauseResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResumeReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResumeResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CloneRootfsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CloneRootfsResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChownClonedRootfsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChownClonedRootfsResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RevertClonedRootfsChownReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sysboxMgrProtobuf_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RevertClonedRootfsChownResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_sysboxMgrProtobuf_proto_rawDesc, + NumEnums: 0, + NumMessages: 33, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_sysboxMgrProtobuf_proto_goTypes, + DependencyIndexes: file_sysboxMgrProtobuf_proto_depIdxs, + MessageInfos: file_sysboxMgrProtobuf_proto_msgTypes, + }.Build() + File_sysboxMgrProtobuf_proto = out.File + file_sysboxMgrProtobuf_proto_rawDesc = nil + file_sysboxMgrProtobuf_proto_goTypes = nil + file_sysboxMgrProtobuf_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// SysboxMgrStateChannelClient is the client API for SysboxMgrStateChannel service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type SysboxMgrStateChannelClient interface { + // Container registration + Register(ctx context.Context, in *RegisterReq, opts ...grpc.CallOption) (*RegisterResp, error) + // Container Update + Update(ctx context.Context, in *UpdateReq, opts ...grpc.CallOption) (*UpdateResp, error) + // Container Unregistration + Unregister(ctx context.Context, in *UnregisterReq, opts ...grpc.CallOption) (*UnregisterResp, error) + // Subuid(gid) allocation request + SubidAlloc(ctx context.Context, in *SubidAllocReq, opts ...grpc.CallOption) (*SubidAllocResp, error) + // Mount source prep request + PrepMounts(ctx context.Context, in *MountPrepReq, opts ...grpc.CallOption) (*MountPrepResp, error) + // Mount request + ReqMounts(ctx context.Context, in *MountReq, opts ...grpc.CallOption) (*MountResp, error) + // Shiftfs mark request + ReqShiftfsMark(ctx context.Context, in *ShiftfsMarkReq, opts ...grpc.CallOption) (*ShiftfsMarkResp, error) + // FsState request + ReqFsState(ctx context.Context, in *FsStateReq, opts ...grpc.CallOption) (*FsStateResp, error) + // Pause request + Pause(ctx context.Context, in *PauseReq, opts ...grpc.CallOption) (*PauseResp, error) + // Resume request + Resume(ctx context.Context, in *ResumeReq, opts ...grpc.CallOption) (*ResumeResp, error) + // Clone rootfs request + ReqCloneRootfs(ctx context.Context, in *CloneRootfsReq, opts ...grpc.CallOption) (*CloneRootfsResp, error) + // Chown cloned rootfs request + ChownClonedRootfs(ctx context.Context, in *ChownClonedRootfsReq, opts ...grpc.CallOption) (*ChownClonedRootfsResp, error) + // Revert cloned rootfs chown + RevertClonedRootfsChown(ctx context.Context, in *RevertClonedRootfsChownReq, opts ...grpc.CallOption) (*RevertClonedRootfsChownResp, error) +} + +type sysboxMgrStateChannelClient struct { + cc grpc.ClientConnInterface +} + +func NewSysboxMgrStateChannelClient(cc grpc.ClientConnInterface) SysboxMgrStateChannelClient { + return &sysboxMgrStateChannelClient{cc} +} + +func (c *sysboxMgrStateChannelClient) Register(ctx context.Context, in *RegisterReq, opts ...grpc.CallOption) (*RegisterResp, error) { + out := new(RegisterResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/Register", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) Update(ctx context.Context, in *UpdateReq, opts ...grpc.CallOption) (*UpdateResp, error) { + out := new(UpdateResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/Update", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) Unregister(ctx context.Context, in *UnregisterReq, opts ...grpc.CallOption) (*UnregisterResp, error) { + out := new(UnregisterResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/Unregister", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) SubidAlloc(ctx context.Context, in *SubidAllocReq, opts ...grpc.CallOption) (*SubidAllocResp, error) { + out := new(SubidAllocResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/SubidAlloc", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) PrepMounts(ctx context.Context, in *MountPrepReq, opts ...grpc.CallOption) (*MountPrepResp, error) { + out := new(MountPrepResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/PrepMounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) ReqMounts(ctx context.Context, in *MountReq, opts ...grpc.CallOption) (*MountResp, error) { + out := new(MountResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/ReqMounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) ReqShiftfsMark(ctx context.Context, in *ShiftfsMarkReq, opts ...grpc.CallOption) (*ShiftfsMarkResp, error) { + out := new(ShiftfsMarkResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/ReqShiftfsMark", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) ReqFsState(ctx context.Context, in *FsStateReq, opts ...grpc.CallOption) (*FsStateResp, error) { + out := new(FsStateResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/ReqFsState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) Pause(ctx context.Context, in *PauseReq, opts ...grpc.CallOption) (*PauseResp, error) { + out := new(PauseResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/Pause", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) Resume(ctx context.Context, in *ResumeReq, opts ...grpc.CallOption) (*ResumeResp, error) { + out := new(ResumeResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/Resume", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) ReqCloneRootfs(ctx context.Context, in *CloneRootfsReq, opts ...grpc.CallOption) (*CloneRootfsResp, error) { + out := new(CloneRootfsResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/ReqCloneRootfs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) ChownClonedRootfs(ctx context.Context, in *ChownClonedRootfsReq, opts ...grpc.CallOption) (*ChownClonedRootfsResp, error) { + out := new(ChownClonedRootfsResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/ChownClonedRootfs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *sysboxMgrStateChannelClient) RevertClonedRootfsChown(ctx context.Context, in *RevertClonedRootfsChownReq, opts ...grpc.CallOption) (*RevertClonedRootfsChownResp, error) { + out := new(RevertClonedRootfsChownResp) + err := c.cc.Invoke(ctx, "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/protobuf.sysboxMgrStateChannel/RevertClonedRootfsChown", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SysboxMgrStateChannelServer is the server API for SysboxMgrStateChannel service. +type SysboxMgrStateChannelServer interface { + // Container registration + Register(context.Context, *RegisterReq) (*RegisterResp, error) + // Container Update + Update(context.Context, *UpdateReq) (*UpdateResp, error) + // Container Unregistration + Unregister(context.Context, *UnregisterReq) (*UnregisterResp, error) + // Subuid(gid) allocation request + SubidAlloc(context.Context, *SubidAllocReq) (*SubidAllocResp, error) + // Mount source prep request + PrepMounts(context.Context, *MountPrepReq) (*MountPrepResp, error) + // Mount request + ReqMounts(context.Context, *MountReq) (*MountResp, error) + // Shiftfs mark request + ReqShiftfsMark(context.Context, *ShiftfsMarkReq) (*ShiftfsMarkResp, error) + // FsState request + ReqFsState(context.Context, *FsStateReq) (*FsStateResp, error) + // Pause request + Pause(context.Context, *PauseReq) (*PauseResp, error) + // Resume request + Resume(context.Context, *ResumeReq) (*ResumeResp, error) + // Clone rootfs request + ReqCloneRootfs(context.Context, *CloneRootfsReq) (*CloneRootfsResp, error) + // Chown cloned rootfs request + ChownClonedRootfs(context.Context, *ChownClonedRootfsReq) (*ChownClonedRootfsResp, error) + // Revert cloned rootfs chown + RevertClonedRootfsChown(context.Context, *RevertClonedRootfsChownReq) (*RevertClonedRootfsChownResp, error) +} + +// UnimplementedSysboxMgrStateChannelServer can be embedded to have forward compatible implementations. +type UnimplementedSysboxMgrStateChannelServer struct { +} + +func (*UnimplementedSysboxMgrStateChannelServer) Register(context.Context, *RegisterReq) (*RegisterResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) Update(context.Context, *UpdateReq) (*UpdateResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) Unregister(context.Context, *UnregisterReq) (*UnregisterResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Unregister not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) SubidAlloc(context.Context, *SubidAllocReq) (*SubidAllocResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubidAlloc not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) PrepMounts(context.Context, *MountPrepReq) (*MountPrepResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method PrepMounts not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) ReqMounts(context.Context, *MountReq) (*MountResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReqMounts not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) ReqShiftfsMark(context.Context, *ShiftfsMarkReq) (*ShiftfsMarkResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReqShiftfsMark not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) ReqFsState(context.Context, *FsStateReq) (*FsStateResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReqFsState not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) Pause(context.Context, *PauseReq) (*PauseResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Pause not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) Resume(context.Context, *ResumeReq) (*ResumeResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Resume not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) ReqCloneRootfs(context.Context, *CloneRootfsReq) (*CloneRootfsResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReqCloneRootfs not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) ChownClonedRootfs(context.Context, *ChownClonedRootfsReq) (*ChownClonedRootfsResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChownClonedRootfs not implemented") +} +func (*UnimplementedSysboxMgrStateChannelServer) RevertClonedRootfsChown(context.Context, *RevertClonedRootfsChownReq) (*RevertClonedRootfsChownResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method RevertClonedRootfsChown not implemented") +} + +func RegisterSysboxMgrStateChannelServer(s *grpc.Server, srv SysboxMgrStateChannelServer) { + s.RegisterService(&_SysboxMgrStateChannel_serviceDesc, srv) +} + +func _SysboxMgrStateChannel_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).Register(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/Register", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).Register(ctx, req.(*RegisterReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/Update", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).Update(ctx, req.(*UpdateReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_Unregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnregisterReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).Unregister(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/Unregister", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).Unregister(ctx, req.(*UnregisterReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_SubidAlloc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubidAllocReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).SubidAlloc(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/SubidAlloc", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).SubidAlloc(ctx, req.(*SubidAllocReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_PrepMounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MountPrepReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).PrepMounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/PrepMounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).PrepMounts(ctx, req.(*MountPrepReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_ReqMounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MountReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).ReqMounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/ReqMounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).ReqMounts(ctx, req.(*MountReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_ReqShiftfsMark_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ShiftfsMarkReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).ReqShiftfsMark(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/ReqShiftfsMark", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).ReqShiftfsMark(ctx, req.(*ShiftfsMarkReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_ReqFsState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FsStateReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).ReqFsState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/ReqFsState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).ReqFsState(ctx, req.(*FsStateReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_Pause_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PauseReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).Pause(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/Pause", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).Pause(ctx, req.(*PauseReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_Resume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResumeReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).Resume(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/Resume", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).Resume(ctx, req.(*ResumeReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_ReqCloneRootfs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CloneRootfsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).ReqCloneRootfs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/ReqCloneRootfs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).ReqCloneRootfs(ctx, req.(*CloneRootfsReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_ChownClonedRootfs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChownClonedRootfsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).ChownClonedRootfs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/ChownClonedRootfs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).ChownClonedRootfs(ctx, req.(*ChownClonedRootfsReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _SysboxMgrStateChannel_RevertClonedRootfsChown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RevertClonedRootfsChownReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SysboxMgrStateChannelServer).RevertClonedRootfsChown(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "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/protobuf.sysboxMgrStateChannel/RevertClonedRootfsChown", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SysboxMgrStateChannelServer).RevertClonedRootfsChown(ctx, req.(*RevertClonedRootfsChownReq)) + } + return interceptor(ctx, in, info, handler) +} + +var _SysboxMgrStateChannel_serviceDesc = grpc.ServiceDesc{ + ServiceName: "protobuf.sysboxMgrStateChannel", + HandlerType: (*SysboxMgrStateChannelServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Register", + Handler: _SysboxMgrStateChannel_Register_Handler, + }, + { + MethodName: "Update", + Handler: _SysboxMgrStateChannel_Update_Handler, + }, + { + MethodName: "Unregister", + Handler: _SysboxMgrStateChannel_Unregister_Handler, + }, + { + MethodName: "SubidAlloc", + Handler: _SysboxMgrStateChannel_SubidAlloc_Handler, + }, + { + MethodName: "PrepMounts", + Handler: _SysboxMgrStateChannel_PrepMounts_Handler, + }, + { + MethodName: "ReqMounts", + Handler: _SysboxMgrStateChannel_ReqMounts_Handler, + }, + { + MethodName: "ReqShiftfsMark", + Handler: _SysboxMgrStateChannel_ReqShiftfsMark_Handler, + }, + { + MethodName: "ReqFsState", + Handler: _SysboxMgrStateChannel_ReqFsState_Handler, + }, + { + MethodName: "Pause", + Handler: _SysboxMgrStateChannel_Pause_Handler, + }, + { + MethodName: "Resume", + Handler: _SysboxMgrStateChannel_Resume_Handler, + }, + { + MethodName: "ReqCloneRootfs", + Handler: _SysboxMgrStateChannel_ReqCloneRootfs_Handler, + }, + { + MethodName: "ChownClonedRootfs", + Handler: _SysboxMgrStateChannel_ChownClonedRootfs_Handler, + }, + { + MethodName: "RevertClonedRootfsChown", + Handler: _SysboxMgrStateChannel_RevertClonedRootfsChown_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "sysboxMgrProtobuf.proto", +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-zfs/dstack-zfs/0001-Define-strndupa-if-it-does-not-exist.patch b/os/yocto/layers/meta-dstack/recipes-core/dstack-zfs/dstack-zfs/0001-Define-strndupa-if-it-does-not-exist.patch new file mode 100644 index 000000000..80955b3ca --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-zfs/dstack-zfs/0001-Define-strndupa-if-it-does-not-exist.patch @@ -0,0 +1,34 @@ +From cc0cd6f71f6ef96fca2d7b730a3f0f6722fec696 Mon Sep 17 00:00:00 2001 +From: Khem Raj +Date: Sat, 7 May 2022 12:15:22 -0700 +Subject: [PATCH] Define strndupa if it does not exist + +musl e.g. does not supply strndupa, unlike glibc + +Upstream-Status: Pending +Signed-off-by: Khem Raj + +--- + etc/systemd/system-generators/zfs-mount-generator.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/etc/systemd/system-generators/zfs-mount-generator.c b/etc/systemd/system-generators/zfs-mount-generator.c +index f4c6c26..255bee4 100644 +--- a/etc/systemd/system-generators/zfs-mount-generator.c ++++ b/etc/systemd/system-generators/zfs-mount-generator.c +@@ -193,6 +193,15 @@ fopenat(int dirfd, const char *pathname, int flags, + return (fdopen(fd, stream_mode)); + } + ++#ifndef strndupa ++#define strndupa(s, n) \ ++ (__extension__ ({const char *__in = (s); \ ++ size_t __len = strnlen (__in, (n)) + 1; \ ++ char *__out = (char *) alloca (__len); \ ++ __out[__len-1] = '\0'; \ ++ (char *) memcpy (__out, __in, __len-1);})) ++#endif ++ + static int + line_worker(char *line, const char *cachefile) + { diff --git a/os/yocto/layers/meta-dstack/recipes-core/dstack-zfs/dstack-zfs_2.4.0.bb b/os/yocto/layers/meta-dstack/recipes-core/dstack-zfs/dstack-zfs_2.4.0.bb new file mode 100644 index 000000000..ced85263a --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/dstack-zfs/dstack-zfs_2.4.0.bb @@ -0,0 +1,78 @@ +SUMMARY = "OpenZFS on Linux and FreeBSD" +DESCRIPTION = "OpenZFS on Linux and FreeBSD" +LICENSE = "CDDL-1.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=7087caaf1dc8a2856585619f4a787faa" +HOMEPAGE = "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/openzfs/zfs" + +SRCREV = "743334913e5a5f60baf287bcc6d8a23515b02ac5" +SRC_URI = "git://github.com/openzfs/zfs;protocol=https;branch=zfs-2.4-release \ + file://0001-Define-strndupa-if-it-does-not-exist.patch \ +" + + +# Using both 'module' and 'autotools' classes seems a bit odd, they both +# define a do_compile function. +# That's why we opt for module-base, also this prevents module splitting. +inherit module-base pkgconfig autotools bash-completion + +DEPENDS = "virtual/kernel zlib util-linux libtirpc openssl curl" + +PACKAGECONFIG ?= "${@bb.utils.filter('DISTRO_FEATURES', 'systemd sysvinit', d)}" + +PACKAGECONFIG[pam] = "--enable-pam --with-pamconfigsdir=${datadir}/pam-configs --with-pammoduledir=${libdir}/security, --disable-pam" +PACKAGECONFIG[systemd] = "--enable-systemd,--disable-systemd," +PACKAGECONFIG[sysvinit] = "--enable-sysvinit,--disable-sysvinit," + +EXTRA_OECONF:append = " \ + --disable-pyzfs \ + --with-linux=${STAGING_KERNEL_DIR} --with-linux-obj=${STAGING_KERNEL_BUILDDIR} \ + --with-mounthelperdir=${base_sbin} \ + --with-udevdir=${base_libdir}/udev \ + --with-systemdunitdir=${systemd_system_unitdir} \ + --with-systemdgeneratordir=${nonarch_base_libdir}/systemd/system-generators \ + --with-systemdpresetdir=${nonarch_base_libdir}/systemd/system-preset \ + --with-systemdmodulesloaddir=${sysconfdir}/module-load.d \ + --without-dracutdir \ +" + +EXTRA_OEMAKE:append = " \ + INSTALL_MOD_PATH=${D}${root_prefix} \ +" + +do_install:append() { + # /usr/share/zfs contains the zfs-tests folder which we do not need: + rm -rf ${D}${datadir}/zfs + + rm -rf ${D}${datadir}/initramfs-tools +} + +FILES:${PN} += "\ + ${nonarch_base_libdir}/modules \ + ${systemd_system_unitdir} \ + ${nonarch_base_libdir}/systemd/system-generators \ + ${nonarch_base_libdir}/systemd/system-preset \ + ${sysconfdir}/modules-load.d/${BPN}.conf \ + ${sysconfdir}/default/${BPN} \ + ${sysconfdir}/sudoers.d/${BPN} \ + ${sysconfdir}/${BPN} \ + ${base_libdir}/udev \ + ${sbindir} \ + ${bindir} \ + ${libexecdir}/${BPN} \ + ${libdir} \ +" + +FILES:${PN}-dev += "\ + ${prefix}/src/zfs-${PV} \ + ${prefix}/src/spl-${PV} \ +" +# Skip buildpaths QA check for kernel modules +INSANE_SKIP:${PN} += "buildpaths" +INSANE_SKIP:${PN}-dbg += "buildpaths" + +# Not yet ported to rv32 +COMPATIBLE_HOST:riscv32 = "null" +# conflicting definition of ABS macro from asm/asm.h from kernel +COMPATIBLE_HOST:mips = "null" +# FTBFS on aarch64 with 6.2+ kernel see https://github.com/openzfs/zfs/issues/14555 +COMPATIBLE_HOST:aarch64 = "null" diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initramfs.bb b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initramfs.bb new file mode 100644 index 000000000..a6a8e1d7c --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initramfs.bb @@ -0,0 +1,31 @@ +PACKAGE_INSTALL = "busybox cryptsetup dstack-initscript" + +# Do not pollute the initrd image with rootfs features +IMAGE_FEATURES = "" + +# Don't allow the initramfs to contain a kernel +PACKAGE_EXCLUDE = "kernel-image-*" + +IMAGE_NAME_SUFFIX ?= "" +IMAGE_LINGUAS = "" +IMAGE_NAME = "dstack-initramfs" + +LICENSE = "MIT" + +IMAGE_FSTYPES = "${INITRAMFS_FSTYPES}" +inherit core-image + +IMAGE_ROOTFS_SIZE = "8192" +IMAGE_ROOTFS_EXTRA_SPACE = "0" + +# Use the same restriction as initramfs-module-install +COMPATIBLE_HOST = '(x86_64.*|i.86.*|arm.*|aarch64.*|loongarch64.*)-(linux.*|freebsd.*)' + +# Remove sysvinit related files in a postprocess function +ROOTFS_POSTPROCESS_COMMAND += "postprocess_initramfs;" + +postprocess_initramfs() { + rm -rf ${IMAGE_ROOTFS}${sysconfdir}/init.d + rm -rf ${IMAGE_ROOTFS}${systemd_system_unitdir} + rm -rf ${IMAGE_ROOTFS}${bindir}/dstack-guest-agent +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initscript.bb b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initscript.bb new file mode 100644 index 000000000..61908cc0c --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initscript.bb @@ -0,0 +1,20 @@ +SUMMARY = "dstack initramfs files" + +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" + +SRC_URI = "file://init" + +FILES:${PN} = "*" + +inherit allarch + +UNPACKDIR = "${WORKDIR}/sources" +S = "${UNPACKDIR}" + +do_install() { + install -d ${D}/ + install -d ${D}/scripts + install -d ${D}/${bindir} + install -m 0755 ${S}/init ${D}/init +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initscript/init b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initscript/init new file mode 100755 index 000000000..968671f60 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-initscript/init @@ -0,0 +1,120 @@ +#!/bin/sh +set -e + +ROOT_DIR=/root +ROOT_DEV="" + +log() { + printf '[init] %s\n' "$*" >&2 +} + +mount_move_all() { + for dir in "$@"; do + mount --move "/$dir" "${ROOT_DIR}/$dir" + done +} + +find_device_by_partlabel() { + label=$1 + + log "searching for PARTLABEL=${label}" + + for entry in /sys/class/block/*; do + [ -f "$entry/partition" ] || continue + uevent="${entry}/uevent" + if [ -f "$uevent" ]; then + current=$(grep -E '^PARTNAME=' "$uevent" 2>/dev/null | head -n1 | cut -d= -f2) + if [ "${current}" = "$label" ]; then + device="/dev/$(basename "$entry")" + if [ -b "$device" ]; then + log "sysfs matched PARTLABEL=${label} at ${device}" + realpath "$device" 2>/dev/null || echo "$device" + return 0 + fi + fi + fi + done + + log "no device found for PARTLABEL=${label}" + return 1 +} + +resolve_root_device() { + if [ -z "$ROOTFS_DEVICE" ]; then + ROOTFS_DEVICE="PARTLABEL=dstack-rootfs" + fi + + if [ "${ROOTFS_DEVICE#PARTLABEL=}" != "$ROOTFS_DEVICE" ]; then + label="${ROOTFS_DEVICE#PARTLABEL=}" + if [ -n "$label" ]; then + device=$(find_device_by_partlabel "$label") + if [ -n "$device" ]; then + log "determined root device via kernel parameter: ${device}" + ROOT_DEV="$device" + return + fi + fi + elif [ "${ROOTFS_DEVICE#/dev/}" != "$ROOTFS_DEVICE" ]; then + ROOT_DEV="$ROOTFS_DEVICE" + return + fi + + log "unable to determine root device from available heuristics" +} + +main() { + export PATH=/sbin:/bin:/usr/sbin:/usr/bin + + mkdir -p /proc + mkdir -p /sys + mkdir -p /run + mount -t proc proc /proc + mount -t sysfs sysfs /sys + mount -t tmpfs tmpfs /run + + mkdir -p /dev + mount -t devtmpfs devtmpfs /dev + + mkdir -p /dev/pts + + log "booting dstack initramfs" + + ROOTFS_DEVICE="" + for param in $(cat /proc/cmdline); do + case "$param" in + "dstack.rootfs_hash="*) + ROOT_HASH="${param#*=}" + ;; + "dstack.rootfs_size="*) + DATA_SIZE="${param#*=}" + ;; + "dstack.rootfs_device="*) + ROOTFS_DEVICE="${param#*=}" + ;; + *) ;; + esac + done + + resolve_root_device + + if [ -z "$ROOT_DEV" ]; then + log "failed to determine root device" + exit 1 + fi + log "resolved root device: ${ROOT_DEV}" + + if [ -z "${ROOT_HASH}" ] || [ -z "${DATA_SIZE}" ]; then + log "missing dm-verity parameters; refusing to continue" + exit 1 + fi + veritysetup open "${ROOT_DEV}" rootfs "${ROOT_DEV}" "${ROOT_HASH}" --hash-offset="${DATA_SIZE}" + log "mounting verified rootfs from /dev/mapper/rootfs" + mount -t squashfs /dev/mapper/rootfs ${ROOT_DIR} + mount_move_all sys proc dev run + log "switching root to ${ROOT_DIR}" + exec switch_root ${ROOT_DIR} /sbin/init + log "switch_root failed" + exit 1 +} + +main "$@" diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-base.inc b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-base.inc new file mode 100644 index 000000000..4862014ff --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-base.inc @@ -0,0 +1,191 @@ +inherit core-image + +IMAGE_BASENAME = "${PN}" + +IMAGE_INSTALL = "\ + ${VIRTUAL-RUNTIME_base-utils} \ + ${ROOTFS_BOOTSTRAP_INSTALL} \ + base-files \ + base-passwd \ + systemd \ + netbase \ + iptables \ + nftables \ + docker-moby \ + docker-compose \ + dstack-guest \ + wireguard-tools \ + cryptsetup \ + curl \ + jq \ + chrony \ + chronyc \ + dstack-zfs \ + dstack-sysbox \ + kernel-module-tun \ + kernel-module-fuse \ + kernel-module-br-netfilter \ + kernel-module-xt-mark \ + kernel-module-xt-connmark \ + kernel-module-xt-comment \ + kernel-module-xt-multiport \ + kernel-module-xt-statistic \ + kernel-module-xt-redirect \ + kernel-module-xt-tcpmss \ + kernel-module-xt-ct \ + kernel-module-xt-log \ + kernel-module-xt-limit \ + kernel-module-nf-tables \ + kernel-module-nft-compat \ + kernel-module-nft-nat \ + kernel-module-nft-chain-nat \ + kernel-module-nft-masq \ + kernel-module-nft-redir \ + kernel-module-nft-ct \ + kernel-module-nft-log \ + kernel-module-nft-limit \ + kernel-module-nft-reject \ + kernel-module-nft-reject-inet \ + kernel-module-nft-hash \ + kernel-module-ip-set \ + kernel-module-ip-set-hash-ip \ + kernel-module-ip-set-hash-net \ + kernel-module-ip-set-hash-ipport \ + kernel-module-ip-set-hash-ipportip \ + kernel-module-ip-set-hash-ipportnet \ + kernel-module-ip-set-hash-netiface \ + kernel-module-ip-set-bitmap-ip \ + kernel-module-ip-set-bitmap-port \ + kernel-module-ip-set-list-set \ + kernel-module-xt-set \ + kernel-module-xt-nflog \ + kernel-module-xt-physdev \ + fuse3 \ + fuse3-utils \ + pigz \ + xfsprogs \ + e2fsprogs \ + e2fsprogs-resize2fs \ + gptfdisk \ + parted \ + tpm2-tools \ +" + +IMAGE_NAME_SUFFIX ?= "" +IMAGE_LINGUAS = "" +INITRAMFS_MAXSIZE = "1000000" + +LICENSE = "MIT" + +IMAGE_CLASSES += "dm-verity-img" + +DM_VERITY_IMAGE = "${PN}" +DM_VERITY_IMAGE_TYPE = "squashfs" +DM_VERITY_SEPARATE_HASH = "0" +DM_VERITY_REPRODUCIBLE = "1" + +IMAGE_FSTYPES = "cpio squashfs" +CONVERSIONTYPES += "verity" +IMAGE_TYPES += "squashfs.verity" + +EXTRA_IMAGE_FEATURES = "read-only-rootfs" + +IMAGE_ROOTFS_SIZE = "8192" +IMAGE_ROOTFS_EXTRA_SPACE = "0" + +# Use the same restriction as initramfs-live-install +COMPATIBLE_HOST = "x86_64.*-linux" + +# Remove sysvinit related files in a postprocess function +ROOTFS_POSTPROCESS_COMMAND += "remove_sysvinit_files;" +ROOTFS_POSTPROCESS_COMMAND += "symlink_lib64;" +IMAGE_FEATURES[validitems] += "nologin" +ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains_any("IMAGE_FEATURES", [ 'nologin' ], "disable_login", "",d)}' +ROOTFS_POSTPROCESS_COMMAND += "install_docker_config;" +ROOTFS_POSTPROCESS_COMMAND += "mkdirs;" + + +remove_sysvinit_files() { + # Remove /etc/init.d directory and its contents + rm -rf ${IMAGE_ROOTFS}${sysconfdir}/init.d + + # Remove /etc/rc*.d directories and their contents + for d in ${IMAGE_ROOTFS}${sysconfdir}/rc*.d; do + rm -rf $d + done + + # Remove other sysvinit specific files + rm -f ${IMAGE_ROOTFS}${sysconfdir}/inittab +} + +symlink_lib64() { + ln -s lib ${IMAGE_ROOTFS}/lib64 +} + +disable_login() { + for srv in getty getty-pre; do + rm -f ${IMAGE_ROOTFS}/etc/systemd/system/${srv}.target + rm -f ${IMAGE_ROOTFS}/usr/lib/systemd/system/${srv}.target + done + for srv in autovt container-getty console-getty getty-generator serial-getty getty; do + rm -f ${IMAGE_ROOTFS}/etc/systemd/system/${srv}.service + rm -f ${IMAGE_ROOTFS}/etc/systemd/system/${srv}@.service + rm -f ${IMAGE_ROOTFS}/usr/lib/systemd/system/${srv}.service + rm -f ${IMAGE_ROOTFS}/usr/lib/systemd/system/${srv}@.service + done + + # Login/console related + rm -rf ${IMAGE_ROOTFS}/usr/sbin/getty + rm -rf ${IMAGE_ROOTFS}/usr/sbin/agetty + rm -rf ${IMAGE_ROOTFS}/usr/bin/login + rm -rf ${IMAGE_ROOTFS}/usr/bin/loginctl + rm -rf ${IMAGE_ROOTFS}/etc/systemd/system/getty.target.wants + + # Console fonts and keymaps + rm -rf ${IMAGE_ROOTFS}/usr/share/consolefonts + rm -rf ${IMAGE_ROOTFS}/usr/share/keymaps + + # Systemd components we don't need + rm -rf ${IMAGE_ROOTFS}/usr/bin/systemd-tty-ask-password-agent + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/debug-shell.service + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/suspend.target + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/systemd-hibernate.service + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/systemd-suspend-then-hibernate.service + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/systemd-hibernate-resume + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system-generators/systemd-getty-generator + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/systemd-logind + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/systemd-logind.service + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/multi-user.target.wants/systemd-logind.service + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/logind.conf.d + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/system/graphical.target.wants + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/user/graphical-session.target + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/user/graphical-session-pre.target + rm -rf ${IMAGE_ROOTFS}/usr/lib/systemd/user/bluetooth.target + + # Polkit + rm -rf ${IMAGE_ROOTFS}/usr/share/polkit-1 + + # Factory defaults + rm -rf ${IMAGE_ROOTFS}/usr/share/factory + + > ${IMAGE_ROOTFS}/etc/securetty +} + +DOCKER_DAEMON_JSON ?= "${THISDIR}/files/docker-daemon.json" + +install_docker_config() { + install -d ${IMAGE_ROOTFS}${sysconfdir}/docker + install -m 0644 ${DOCKER_DAEMON_JSON} ${IMAGE_ROOTFS}${sysconfdir}/docker/daemon.json +} + +mkdirs() { + mkdir -p ${IMAGE_ROOTFS}/dev + mkdir -p ${IMAGE_ROOTFS}/proc + mkdir -p ${IMAGE_ROOTFS}/sys + mkdir -p ${IMAGE_ROOTFS}/run + mkdir -p ${IMAGE_ROOTFS}/dstack + mkdir -p ${IMAGE_ROOTFS}/etc/wireguard + mkdir -p ${IMAGE_ROOTFS}/var/lib/docker + mkdir -p ${IMAGE_ROOTFS}/var/lib/containerd + ln -sf dstack ${IMAGE_ROOTFS}/tapp +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-dev.inc b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-dev.inc new file mode 100644 index 000000000..1481a98ac --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-dev.inc @@ -0,0 +1,2 @@ +IMAGE_INSTALL += "packagegroup-core-ssh-openssh strace tcpdump gdb gdbserver vim" +EXTRA_IMAGE_FEATURES += "allow-root-login post-install-logging" diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-nvidia.inc b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-nvidia.inc new file mode 100644 index 000000000..704ccaed0 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-nvidia.inc @@ -0,0 +1,19 @@ +NVIDIA_GROUP = "acpid \ + nvidia \ + nvidia-firmware \ + nvidia-gpu-detect \ + nvidia-persistenced \ + nvidia-fabricmanager \ + libnvidia-nscq \ + nvattest \ + nvidia-container-toolkit \ + kernel-module-video \ + numactl \ +" +# Do not autoload NVIDIA modules on every boot. The default image also runs on +# GPU-less hosts, so GPU services load these modules only after hardware +# detection succeeds. +KERNEL_MODULE_AUTOLOAD:append = " video" +IMAGE_INSTALL:append = " ${NVIDIA_GROUP}" + +DOCKER_DAEMON_JSON = "${THISDIR}/files/docker-daemon-nvidia.json" diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-prod.inc b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-prod.inc new file mode 100644 index 000000000..a446e0396 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs-prod.inc @@ -0,0 +1 @@ +IMAGE_FEATURES += "nologin" diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs.bb b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs.bb new file mode 100644 index 000000000..419933b1d --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-rootfs.bb @@ -0,0 +1,17 @@ +# Unified dstack rootfs image +# Use DSTACK_FLAVOR (via multiconfig) to select variant: +# prod, dev + +# Default flavor settings (can be overridden by multiconfig) +DSTACK_FLAVOR ?= "prod" +DSTACK_DEV ?= "0" + +# Base configuration +include dstack-rootfs-base.inc + +# Production or development mode +include ${@'dstack-rootfs-dev.inc' if d.getVar('DSTACK_DEV') == '1' else 'dstack-rootfs-prod.inc'} + +# NVIDIA support is included in all images; services are gated at runtime by +# hardware-detection ExecCondition= checks so the same image works without GPUs. +include dstack-rootfs-nvidia.inc diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/dstack-uki.bb b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-uki.bb new file mode 100644 index 000000000..7b871e364 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/dstack-uki.bb @@ -0,0 +1,144 @@ +# Unified Kernel Image (UKI) for dstack +# +# This recipe generates a UKI containing kernel, initramfs, and cmdline +# with dm-verity root hash for GCP deployment. + +SUMMARY = "dstack Unified Kernel Image" +LICENSE = "MIT" + +DEPENDS = "systemd-boot systemd-boot-native virtual/kernel python3-pefile-native" + +inherit image-artifact-names +require conf/image-uefi.conf + +# Initramfs settings +INITRAMFS_IMAGE = "dstack-initramfs" +INITRAMFS_FSTYPES = "cpio.gz" + +# Kernel settings +KERNEL_IMAGETYPE = "bzImage" + +# Base kernel cmdline (verity hash added dynamically) +UKI_CMDLINE_BASE = "console=ttyS0 init=/init panic=1 net.ifnames=0 biosdevname=0 \ +mce=off oops=panic pci=noearly pci=nommconf random.trust_cpu=y random.trust_bootloader=n \ +tsc=reliable no-kvmclock" + +# Flavor settings (should match dstack-rootfs.bb, set via multiconfig) +DSTACK_FLAVOR ?= "prod" + +# Verity image to get hash from - always use dstack-rootfs (same PN, different multiconfig) +VERITY_IMAGE = "dstack-rootfs" +VERITY_TYPE = "squashfs" + +UKI_FILENAME = "dstack-uki.efi" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" +do_install[noexec] = "1" + +# Dependencies +do_uki[depends] += "systemd-boot:do_deploy virtual/kernel:do_deploy" +do_uki[depends] += "${INITRAMFS_IMAGE}:do_image_complete" +do_uki[depends] += "${VERITY_IMAGE}:do_image_complete" +do_uki[depends] += "systemd-boot-native:do_populate_sysroot python3-pefile-native:do_populate_sysroot" + +python do_uki() { + import os + import bb.process + + deploy_dir = d.getVar('DEPLOY_DIR_IMAGE') + target_arch = d.getVar('EFI_ARCH') + + # Find the EFI stub + stub = os.path.join(deploy_dir, f"linux{target_arch}.efi.stub") + if not os.path.exists(stub): + bb.fatal(f"EFI stub not found: {stub}") + + # Find kernel + kernel = os.path.join(deploy_dir, d.getVar('KERNEL_IMAGETYPE')) + if not os.path.exists(kernel): + bb.fatal(f"Kernel not found: {kernel}") + + # Find initramfs + initramfs_image = d.getVar('INITRAMFS_IMAGE') + machine = d.getVar('MACHINE') + initramfs_fstypes = d.getVar('INITRAMFS_FSTYPES') + initrd = os.path.join(deploy_dir, f"{initramfs_image}-{machine}.{initramfs_fstypes}") + if not os.path.exists(initrd): + bb.fatal(f"Initramfs not found: {initrd}") + + # Read verity hash + staging_verity_dir = d.getVar('STAGING_VERITY_DIR') or d.expand('${TMPDIR}/work-shared/${MACHINE}/dm-verity') + verity_image = d.getVar('VERITY_IMAGE') + verity_type = d.getVar('VERITY_TYPE') + verity_env = os.path.join(staging_verity_dir, f"{verity_image}.{verity_type}.verity.env") + + root_hash = "" + data_size = "" + + if os.path.exists(verity_env): + with open(verity_env, 'r') as f: + for line in f: + line = line.strip() + if line.startswith('ROOT_HASH='): + root_hash = line.split('=', 1)[1] + elif line.startswith('DATA_SIZE='): + data_size = line.split('=', 1)[1] + bb.note(f"Read verity env: root_hash={root_hash}, data_size={data_size}") + else: + bb.fatal(f"Verity env file not found: {verity_env}") + + if not root_hash or not data_size: + bb.fatal(f"ROOT_HASH/DATA_SIZE missing from verity env: {verity_env}") + + # Build cmdline + cmdline_base = d.getVar('UKI_CMDLINE_BASE') + cmdline = f"{cmdline_base} dstack.rootfs_hash={root_hash} dstack.rootfs_size={data_size}" + bb.note(f"UKI cmdline: {cmdline}") + + # Output path + output = os.path.join(deploy_dir, d.getVar('UKI_FILENAME')) + + # Build ukify command with proper Python paths + native_sysroot = d.getVar('RECIPE_SYSROOT_NATIVE') + staging_libdir = d.getVar('STAGING_LIBDIR_NATIVE') + + # Find Python version directory for native packages (version-agnostic) + import glob + _pyglob = sorted(glob.glob(os.path.join(staging_libdir, 'python3.*', 'site-packages'))) + python_sitepackages = _pyglob[-1] if _pyglob else os.path.join(staging_libdir, 'python3', 'site-packages') + + # Set environment for ukify + env = os.environ.copy() + env['PYTHONPATH'] = python_sitepackages + + ukify_path = os.path.join(native_sysroot, 'usr', 'bin', 'ukify') + # Pass an argument list (no shell) so values like cmdline don't need quoting + # and can't be split/expanded by the shell. + ukify_cmd = [ + ukify_path, "build", + f"--efi-arch={target_arch}", + f"--stub={stub}", + f"--linux={kernel}", + f"--initrd={initrd}", + f"--cmdline={cmdline}", + f"--tools={native_sysroot}/usr/lib/systemd/tools", + f"--output={output}", + ] + + bb.note(f"Running: {' '.join(ukify_cmd)}") + bb.note(f"PYTHONPATH: {python_sitepackages}") + + import subprocess + result = subprocess.run(ukify_cmd, capture_output=True, text=True, env=env) + if result.stdout: + bb.note(result.stdout) + if result.stderr: + bb.note(result.stderr) + if result.returncode != 0: + bb.fatal(f"ukify failed with exit code {result.returncode}") + + bb.note(f"UKI created: {output}") +} + +addtask uki before do_build diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/files/docker-daemon-nvidia.json b/os/yocto/layers/meta-dstack/recipes-core/images/files/docker-daemon-nvidia.json new file mode 100644 index 000000000..a235187f9 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/files/docker-daemon-nvidia.json @@ -0,0 +1,16 @@ +{ + "log-driver": "json-file", + "log-opts": { + "max-size": "100m", + "max-file": "10" + }, + "runtimes": { + "nvidia": { + "args": [], + "path": "nvidia-container-runtime" + }, + "sysbox-runc": { + "path": "/usr/bin/sysbox-runc" + } + } +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/images/files/docker-daemon.json b/os/yocto/layers/meta-dstack/recipes-core/images/files/docker-daemon.json new file mode 100644 index 000000000..c4db4d44c --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/images/files/docker-daemon.json @@ -0,0 +1,12 @@ +{ + "log-driver": "json-file", + "log-opts": { + "max-size": "100m", + "max-file": "10" + }, + "runtimes": { + "sysbox-runc": { + "path": "/usr/bin/sysbox-runc" + } + } +} diff --git a/os/yocto/layers/meta-dstack/recipes-core/ovmf/ovmf%.bbappend b/os/yocto/layers/meta-dstack/recipes-core/ovmf/ovmf%.bbappend new file mode 100644 index 000000000..b75d73f41 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/ovmf/ovmf%.bbappend @@ -0,0 +1,9 @@ +do_deploy:class-target:append() { + for i in \ + ovmf \ + ovmf.code \ + ovmf.vars \ + ; do + cp ${WORKDIR}/ovmf/$i.fd ${DEPLOYDIR}/ + done +} \ No newline at end of file diff --git a/os/yocto/layers/meta-dstack/recipes-core/systemd/files/0001-core-suppress-ephemeral-status-output.patch b/os/yocto/layers/meta-dstack/recipes-core/systemd/files/0001-core-suppress-ephemeral-status-output.patch new file mode 100644 index 000000000..3ef6e8e6a --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/systemd/files/0001-core-suppress-ephemeral-status-output.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kevin Wang +Date: Thu, 2 Jul 2026 00:00:00 -0700 +Subject: [PATCH] core: suppress ephemeral status output + +Ephemeral status output contains in-progress job updates such as: + + A start job is running for ... + +These updates are intended to refresh the same console line, but serial +log capture records each refresh as a separate line. Suppress only +ephemeral status messages so normal [ OK ] and [FAILED] status output is +preserved. + +Upstream-Status: Inappropriate [dstack-specific serial console policy] +Signed-off-by: Kevin Wang +--- + src/core/manager.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/core/manager.c b/src/core/manager.c +index 3b87896654..aa68edee1d 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -4539,6 +4539,13 @@ static bool manager_should_show_status(Manager *m, StatusType type) { + if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING)) + return false; + ++ /* Ephemeral status messages contain in-progress job updates such as ++ * "A start job is running for ...". Serial log capture records those ++ * carriage-return-based refreshes as repeated lines, so suppress them ++ * while preserving normal [ OK ] and [FAILED] status output. */ ++ if (type == STATUS_TYPE_EPHEMERAL) ++ return false; ++ + /* If we cannot find out the status properly, just proceed. */ + if (type < STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0) + return false; +-- +2.43.0 diff --git a/os/yocto/layers/meta-dstack/recipes-core/systemd/systemd_%.bbappend b/os/yocto/layers/meta-dstack/recipes-core/systemd/systemd_%.bbappend new file mode 100644 index 000000000..b374de353 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-core/systemd/systemd_%.bbappend @@ -0,0 +1,32 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SRC_URI:append = " file://0001-core-suppress-ephemeral-status-output.patch" + +do_install:append() { + # Remove systemd-vconsole-setup entirely (no virtual console needed) + rm -f ${D}${systemd_system_unitdir}/sysinit.target.wants/systemd-vconsole-setup.service + rm -f ${D}${systemd_system_unitdir}/systemd-vconsole-setup.service + rm -f ${D}${rootlibexecdir}/systemd/systemd-vconsole-setup + rm -f ${D}${nonarch_libdir}/udev/rules.d/90-vconsole.rules + + # Disable EFI System Partition automount (not needed, causes UNSUPP error) + rm -f ${D}${nonarch_libdir}/systemd/system-generators/systemd-gpt-auto-generator + + # Ensure systemd-resolved waits for /var/volatile tmpfs and tmpfiles setup + install -d ${D}${systemd_system_unitdir}/systemd-resolved.service.d + cat <<'EOF' > ${D}${systemd_system_unitdir}/systemd-resolved.service.d/10-var-volatile.conf +[Unit] +After=systemd-tmpfiles-setup.service var-volatile.mount +Requires=var-volatile.mount +EOF +} + +SYSTEMD_SERVICE:${PN}-vconsole-setup = "" +PACKAGECONFIG:remove = "sysvinit logind" + + +# Create a minimal package with only systemd-socket-proxyd +# This avoids pulling in all of systemd-extra-utils +PACKAGES =+ "${PN}-socket-proxyd" +FILES:${PN}-socket-proxyd = "${nonarch_libdir}/systemd/systemd-socket-proxyd" +RDEPENDS:${PN}-socket-proxyd = "${PN}" diff --git a/os/yocto/layers/meta-dstack/recipes-devtools/fdisk/gptfdisk_%.bbappend b/os/yocto/layers/meta-dstack/recipes-devtools/fdisk/gptfdisk_%.bbappend new file mode 100644 index 000000000..a366cc382 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-devtools/fdisk/gptfdisk_%.bbappend @@ -0,0 +1,2 @@ +# Only build sgdisk, skip cgdisk (requires ncurses) +PACKAGECONFIG = "popt" diff --git a/os/yocto/layers/meta-dstack/recipes-devtools/gcc/libgcc-initial_%.bbappend b/os/yocto/layers/meta-dstack/recipes-devtools/gcc/libgcc-initial_%.bbappend new file mode 100644 index 000000000..c006d6efb --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-devtools/gcc/libgcc-initial_%.bbappend @@ -0,0 +1,19 @@ +python dstack_stub_stdio() { + import os + staging_incdir = d.getVar('STAGING_INCDIR') + stdio = os.path.join(staging_incdir, 'stdio.h') + if not os.path.exists(stdio): + bb.note("Adding minimal stdio.h stub to satisfy libgcc-initial configure") + with open(stdio, 'w') as fh: + fh.write('#ifndef __YOCTO_DUMMY_STDIO__\n') + fh.write('#define __YOCTO_DUMMY_STDIO__\n') + fh.write('typedef int FILE;\n') + fh.write('extern FILE *stdin;\n') + fh.write('extern FILE *stdout;\n') + fh.write('extern FILE *stderr;\n') + fh.write('static inline int printf(const char *fmt, ...) { (void)fmt; return 0; }\n') + fh.write('#endif\n') +} + +do_configure[prefuncs] += "dstack_stub_stdio" +DEBUG_FLAGS = "" diff --git a/os/yocto/layers/meta-dstack/recipes-devtools/gptfdisk/gptfdisk_%.bbappend b/os/yocto/layers/meta-dstack/recipes-devtools/gptfdisk/gptfdisk_%.bbappend new file mode 100644 index 000000000..af3122b28 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-devtools/gptfdisk/gptfdisk_%.bbappend @@ -0,0 +1,2 @@ +# Disable ncurses/cgdisk to avoid linking against libncursesw (not in our images) +PACKAGECONFIG:remove = "ncurses" diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/0001-x86-tdx-select-dma-direct-remap.patch b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/0001-x86-tdx-select-dma-direct-remap.patch new file mode 100644 index 000000000..662dd175c --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/0001-x86-tdx-select-dma-direct-remap.patch @@ -0,0 +1,35 @@ +From 48cf4656e9a23f7f5b4a2f6a0e5b5b2b6d5e5b6d Mon Sep 17 00:00:00 2001 +From: h4x +Date: Mon, 27 Oct 2025 23:52:02 +0000 +Subject: [PATCH] x86/tdx: select DMA_DIRECT_REMAP for encrypted guests + +TDX guests require DMA buffers to be mapped out of the shared (decrypted) +pool in order for shared devices such as NVMe to complete I/O. Without +DMA_DIRECT_REMAP the dma-direct layer falls back to allocations from the +regular page allocator, leaving the memory encrypted and causing every +request to wedge once it hits the host. + +Ensure the TDX guest configuration pulls in DMA_DIRECT_REMAP, which in +turn enables the coherent DMA pool machinery and honours the +`coherent_pool=` kernel parameter. + +Upstream-Status: Inappropriate [TDX guest specific selection] +Signed-off-by: h4x +--- + arch/x86/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 5d57930be347..c1b55a4bf4f0 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -886,6 +886,7 @@ config INTEL_TDX_GUEST + depends on EFI_STUB + depends on PARAVIRT + select ARCH_HAS_CC_PLATFORM ++ select DMA_DIRECT_REMAP + select X86_MEM_ENCRYPT + select X86_MCE + select UNACCEPTED_MEMORY +-- +2.46.0 diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/0002-acpi-sandbox-block-aml-systemmemory-ram-access.patch b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/0002-acpi-sandbox-block-aml-systemmemory-ram-access.patch new file mode 100644 index 000000000..98ce12641 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/0002-acpi-sandbox-block-aml-systemmemory-ram-access.patch @@ -0,0 +1,226 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Paul Meyer +Date: Tue, 17 Feb 2026 10:47:28 +0100 +Subject: [PATCH] drivers/acpi: add BadAML sandbox + +Block AML SystemMemory region accesses that target encrypted (private) +guest RAM. A malicious hypervisor can craft ACPI tables whose AML reads +or writes confidential guest memory via the SystemMemory operation +region handler; this sandbox walks the page tables and denies the access +when the target page is encrypted, logging the decision. + +On a platform without memory encryption the sandbox is a no-op: every +access is treated as unencrypted and allowed. Only denied accesses are +logged unconditionally; allowed accesses use ACPI debug-level output to +avoid flooding the log. + +Ported from the Easy-TEE project (mkosi gcp profile kernel patch). + +Upstream-Status: Inappropriate [confidential-guest hardening] +Signed-off-by: Paul Meyer +--- +diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c +index a390a1c..6383233 100644 +--- a/drivers/acpi/acpica/exregion.c ++++ b/drivers/acpi/acpica/exregion.c +@@ -14,6 +14,8 @@ + #define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME("exregion") + ++#include "sandbox.h" ++ + /******************************************************************************* + * + * FUNCTION: acpi_ex_system_memory_space_handler +@@ -38,6 +40,7 @@ acpi_ex_system_memory_space_handler(u32 function, + u64 *value, + void *handler_context, void *region_context) + { ++ SANDBOX_SECT_START; + acpi_status status = AE_OK; + void *logical_addr_ptr = NULL; + struct acpi_mem_space_context *mem_info = region_context; +@@ -192,6 +195,7 @@ access: + case ACPI_READ: + + *value = 0; ++ SANDBOX_READ_HOOK((u64)logical_addr_ptr, (u64)address); + switch (bit_width) { + case 8: + +@@ -223,6 +227,7 @@ access: + + case ACPI_WRITE: + ++ SANDBOX_WRITE_HOOK((u64)logical_addr_ptr, (u64)address); + switch (bit_width) { + case 8: + +@@ -258,6 +263,7 @@ access: + break; + } + ++ SANDBOX_SECT_END; + return_ACPI_STATUS(status); + } + +diff --git a/drivers/acpi/acpica/sandbox.h b/drivers/acpi/acpica/sandbox.h +new file mode 100644 +index 0000000..34d09ca +--- /dev/null ++++ b/drivers/acpi/acpica/sandbox.h +@@ -0,0 +1,154 @@ ++/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ ++/* SPDX-FileCopyrightText: Satoru Takekoshi, Manami Mori, Takaaki Fukai, ++ * Takahiro Shinagawa */ ++/* SPDX-FileCopyrightText: Edgeless Systems GmbH */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SANDBOX_READ_HOOK(virt_addr, phys_addr) { if (!__sandbox_validate_memory_access(virt_addr, phys_addr, true)) break; } ++#define SANDBOX_WRITE_HOOK(virt_addr, phys_addr) { if (!__sandbox_validate_memory_access(virt_addr, phys_addr, false)) break; } ++#define SANDBOX_SECT_START { __sandbox_section_start(); } ++#define SANDBOX_SECT_END { __sandbox_section_end(); } ++ ++static struct __sandbox_access_log { ++ bool is_read; ++ unsigned long phys_addr; ++ unsigned long virt_addr; ++ bool access_allowed; ++} __sandbox_access_log; ++ ++static void __sandbox_log_enabled(void) ++{ ++ DO_ONCE(pr_info, "SANDBOX: Enabled\n"); ++} ++ ++static unsigned long __sandbox_get_page_table_entry(unsigned long addr) ++{ ++ pgd_t *pgd; ++ p4d_t *p4d; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *pte; ++ ++ pgd = pgd_offset_k(addr); ++ if (pgd_none(*pgd)) { ++ return 0; ++ } ++ ++ p4d = p4d_offset(pgd, addr); ++ if (p4d_none(*p4d)) { ++ return 0; ++ } ++ ++ pud = pud_offset(p4d, addr); ++ if (pud_none(*pud)) { ++ return 0; ++ } ++ ++ /* Check for 1GB huge page */ ++ if (pud_leaf(*pud)) { ++ return pud_val(*pud); ++ } ++ ++ pmd = pmd_offset(pud, addr); ++ if (pmd_none(*pmd)) { ++ return 0; ++ } ++ ++ /* Check for 2MB huge page */ ++ if (pmd_leaf(*pmd)) { ++ return pmd_val(*pmd); ++ } ++ ++ pte = pte_offset_kernel(pmd, addr); ++ if (pte_none(*pte)) { ++ return 0; ++ } ++ ++ return pte_val(*pte); ++} ++ ++static bool __sandbox_is_encrypted_generic(unsigned long virt_addr) ++{ ++ unsigned long val; ++ ++ val = __sandbox_get_page_table_entry((unsigned long)(virt_addr)); ++ if (val) { ++ return val == cc_mkenc(val); ++ } else { ++ ACPI_ERROR((AE_INFO, "SANDBOX: Page table walk failed")); ++ } ++ ++ ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SANDBOX: Falling back to 'encrypted' state\n")); ++ return true; ++} ++ ++static bool __sandbox_validate_memory_access(unsigned long virt_addr, unsigned long phys_addr, bool is_read) ++{ ++ __sandbox_log_enabled(); ++ __sandbox_access_log.is_read = is_read; ++ __sandbox_access_log.phys_addr = phys_addr; ++ __sandbox_access_log.virt_addr = virt_addr; ++ phys_addr &= PAGE_MASK; ++ virt_addr &= PAGE_MASK; ++ ++ cond_resched(); ++ ++ /* ++ * On a platform without memory encryption there is no private guest ++ * memory to protect, so the sandbox is a no-op: treat every access as ++ * unencrypted and allow it. Only confidential guests gate accesses. ++ */ ++ bool encrypted = false; ++ if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) { ++ encrypted = __sandbox_is_encrypted_generic(virt_addr); ++ } ++ ++ cond_resched(); ++ ++ if (!encrypted) { ++ return true; ++ } ++ ++ __sandbox_access_log.access_allowed = false; ++ return false; ++} ++ ++static void __sandbox_section_start(void) ++{ ++ __sandbox_access_log.is_read = true; ++ __sandbox_access_log.phys_addr = 0xdeadbeefcafebabeuL; ++ __sandbox_access_log.virt_addr = 0xdeadbeefcafebabeuL; ++ __sandbox_access_log.access_allowed = true; ++} ++ ++static void __sandbox_section_end(void) ++{ ++ cond_resched(); ++ ++ /* ++ * AML SystemMemory accesses can be frequent, so only the (rare) denied ++ * accesses are logged unconditionally; allowed accesses go to ACPI ++ * debug-level output to avoid flooding the log on every boot/runtime ++ * access while preserving observability when debugging. ++ */ ++ if (!__sandbox_access_log.access_allowed) { ++ ACPI_ERROR((AE_INFO, ++ "SANDBOX: DENIED %s virt=%lx phys=%lx", ++ __sandbox_access_log.is_read ? "r" : "w", ++ (unsigned long)__sandbox_access_log.virt_addr, ++ (unsigned long)__sandbox_access_log.phys_addr)); ++ } else { ++ ACPI_DEBUG_PRINT((ACPI_DB_INFO, ++ "SANDBOX: ALLOWED %s virt=%lx phys=%lx\n", ++ __sandbox_access_log.is_read ? "r" : "w", ++ (unsigned long)__sandbox_access_log.virt_addr, ++ (unsigned long)__sandbox_access_log.phys_addr)); ++ } ++ ++ cond_resched(); ++} diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg new file mode 100644 index 000000000..99f4607ff --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.cfg @@ -0,0 +1,70 @@ +CONFIG_BRIDGE=m +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m + +# nf_tables support (needed by modern iptables-nft backend) +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_NETDEV=y +CONFIG_NFT_COMPAT=m +CONFIG_NFT_NAT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_CT=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_REJECT_INET=m +CONFIG_NFT_HASH=m +CONFIG_BPF_SYSCALL=y +CONFIG_IP_VS=m +CONFIG_SECCOMP=y +CONFIG_SECCOMP_FILTER=y +CONFIG_SECURITY_SELINUX=y +CONFIG_VLAN_8021Q=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_XFRM=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_ALGO=y +# Disable ESP transforms to mitigate DirtyFrag-style page-cache writes via esp4/esp6. +# Keep XFRM userspace support available for non-ESP networking features, but do +# not build the vulnerable ESP protocol handlers. +CONFIG_INET_ESP=n +CONFIG_INET6_ESP=n +CONFIG_XFRM_ESP=n + +# RxRPC is not needed by dstack and is another DirtyFrag trigger path. Keep it +# disabled even if future kernel feature sets would otherwise enable it. +CONFIG_AF_RXRPC=n +CONFIG_RXKAD=n +CONFIG_NETFILTER_XT_MATCH_BPF=y +CONFIG_CRYPTO_SEQIV=y +CONFIG_IPVLAN=m +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_NETFILTER_FAMILY_BRIDGE=y +CONFIG_VETH=y +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y +CONFIG_VXLAN=y +CONFIG_CGROUP_BPF=y + +# ipset support (required by kube-router network policy controller) +CONFIG_IP_SET=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_NETFILTER_XT_SET=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m + +# BLK IO throttling support +CONFIG_BLK_CGROUP=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_DEV_THROTTLING_LOW=y diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.scc b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.scc new file mode 100644 index 000000000..09dda98d0 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-docker.scc @@ -0,0 +1,3 @@ +define KFEATURE_DESCRIPTION "dstack Docker configuration" + +kconf non-hardware dstack-docker.cfg diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-sysbox.cfg b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-sysbox.cfg new file mode 100644 index 000000000..68827bbfc --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-sysbox.cfg @@ -0,0 +1,3 @@ +CONFIG_USER_NS=y +CONFIG_CONFIGFS_FS=y +CONFIG_OVERLAY_FS_METACOPY=y diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-sysbox.scc b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-sysbox.scc new file mode 100644 index 000000000..3c3a8257d --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-sysbox.scc @@ -0,0 +1,3 @@ +define KFEATURE_DESCRIPTION "DStack Sysbox runtime configuration" + +kconf non-hardware dstack-sysbox.cfg diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-tdx.cfg b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-tdx.cfg new file mode 100644 index 000000000..8fe114a9c --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-tdx.cfg @@ -0,0 +1,5 @@ +# In-tree TDX guest driver provides /dev/tdx_guest and the ConfigFS TSM +# report interface (/sys/kernel/config/tsm/report), replacing the former +# out-of-tree mod-tdx-guest module. +CONFIG_TDX_GUEST_DRIVER=y +CONFIG_TSM_REPORTS=y diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-tdx.scc b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-tdx.scc new file mode 100644 index 000000000..5786a660e --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack-tdx.scc @@ -0,0 +1,3 @@ +define KFEATURE_DESCRIPTION "dstack TDX configuration" + +kconf non-hardware dstack-tdx.cfg diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack.cfg b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack.cfg new file mode 100644 index 000000000..54eacd7fc --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack.cfg @@ -0,0 +1,49 @@ +CONFIG_MODULES=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +CONFIG_9P_FS=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_PCI=y +CONFIG_TUN=m +CONFIG_VIRTIO_PCI=y + +# Network NIC drivers. GCP C3/modern instances use gVNIC (the Google gve +# driver); older/virtio instances use virtio-net. Without gve, a C3 CVM gets +# no NIC -> no DHCP -> no network. Build both in. +CONFIG_VIRTIO_NET=y +CONFIG_NET_VENDOR_GOOGLE=y +CONFIG_GVE=y +CONFIG_ISO9660_FS=y +CONFIG_WIREGUARD=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +CONFIG_NR_CPUS=512 + +# BPF +CONFIG_DEBUG_INFO_BTF=y +CONFIG_DEBUG_INFO_BTF_MODULES=y +CONFIG_BPF=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y + +CONFIG_KPROBES=y +# +CONFIG_PM=n +CONFIG_SUSPEND=n +CONFIG_PM_SLEEP=n +CONFIG_HOTPLUG_CPU=n +CONFIG_HOTPLUG_PCI=n +CONFIG_BT=n +CONFIG_MMC=n +CONFIG_SCSI=n +CONFIG_INPUT=n +CONFIG_WLAN=n + +# dm-verity verifies the rootfs in the initramfs (no modules loaded yet), so the +# rootfs hash algorithm must be built-in. linux-yocto-tiny ships SHA256 as a +# module (=m); force it built-in or early verity fails with +# "verity: Cannot initialize hash function (-ENOENT)". +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack.scc b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack.scc new file mode 100644 index 000000000..e277ce0b2 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/files/dstack.scc @@ -0,0 +1,3 @@ +define KFEATURE_DESCRIPTION "dstack configuration" + +kconf non-hardware dstack.cfg diff --git a/os/yocto/layers/meta-dstack/recipes-kernel/linux/linux-yocto%.bbappend b/os/yocto/layers/meta-dstack/recipes-kernel/linux/linux-yocto%.bbappend new file mode 100644 index 000000000..9d7f85ec0 --- /dev/null +++ b/os/yocto/layers/meta-dstack/recipes-kernel/linux/linux-yocto%.bbappend @@ -0,0 +1,73 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +LINUX_VERSION_EXTENSION = "-dstack" + +SRC_URI += "file://dstack-docker.cfg \ + file://dstack-docker.scc \ + file://dstack-tdx.cfg \ + file://dstack-tdx.scc \ + file://dstack-sysbox.cfg \ + file://dstack-sysbox.scc \ + file://dstack.cfg \ + file://dstack.scc" + +# TDX guests need DMA_DIRECT_REMAP for shared (decrypted) coherent DMA so +# devices like NVMe can complete I/O. INTEL_TDX_GUEST does not select it +# upstream (and the symbol is promptless, so a .cfg fragment cannot set it), +# hence this Kconfig patch. Only touches the INTEL_TDX_GUEST Kconfig, so it is +# a no-op on AMD; scoped to the dstack confidential-guest machine. +SRC_URI:append:dstack = " file://0001-x86-tdx-select-dma-direct-remap.patch" + +# Confidential guests are exposed to malicious ACPI tables supplied by the +# host: crafted AML can read/write the guest's encrypted (private) memory +# through the SystemMemory operation region handler. This "BadAML sandbox" +# walks the page tables and denies AML SystemMemory accesses that target +# encrypted pages, logging each decision. Ported from the Easy-TEE project. +# Applied unconditionally: dstack OS always runs inside a TEE, so every +# build needs this hardening (the hook is a runtime no-op when the platform +# reports no memory encryption). +SRC_URI:append = " file://0002-acpi-sandbox-block-aml-systemmemory-ram-access.patch" + +KERNEL_FEATURES:append = " features/cgroups/cgroups.scc \ + features/overlayfs/overlayfs.scc \ + features/netfilter/netfilter.scc \ + features/fuse/fuse.scc \ + features/xfs/xfs.scc \ + cfg/fs/squashfs.scc \ + dstack-docker.scc \ + dstack-sysbox.scc \ + dstack.scc" + +KERNEL_FEATURES:append = " ${@bb.utils.contains("DISTRO_FEATURES", "dm-verity", " features/device-mapper/dm-verity.scc", "" ,d)}" + +# Unified dstack confidential-guest machine. A single kernel image that boots +# on both Intel TDX and AMD SEV-SNP hosts (the kernel detects the platform at +# runtime). The base guest features and the tdx.scc / sev-snp.scc kconf +# fragments are reused from meta-confidential-compute; enabling both TDX and +# SEV here is what makes one image work on either platform. +KMACHINE:dstack ?= "common-pc-64" +COMPATIBLE_MACHINE:dstack = "^dstack$" +KERNEL_FEATURES:append:dstack = " features/scsi/disk.scc \ + cfg/virtio.scc \ + cfg/paravirt_kvm.scc \ + cfg/fs/ext4.scc \ + tdx.scc \ + sev-snp.scc \ + tpm2.scc \ + hyperv.scc \ + security-mitigations.scc \ + disk-encryption.scc \ + dstack-tdx.scc" + +# disk-encryption.scc (above, from meta-confidential-compute) ships dm-crypt +# for the encrypted data volume but explicitly turns CONFIG_DM_VERITY off. The +# dstack rootfs is dm-verity, so re-enable it here -- this is the last dm-verity +# fragment in KERNEL_FEATURES for the dstack machine, so it wins the merge. +KERNEL_FEATURES:append:dstack = " ${@bb.utils.contains("DISTRO_FEATURES", "dm-verity", " features/device-mapper/dm-verity.scc", "", d)}" + +# Enable BTF +KERNEL_DEBUG = "True" + +do_deploy:append() { + install -m 0644 ${B}/.config ${DEPLOYDIR}/kernel-config +} diff --git a/os/yocto/layers/meta-nvidia/README.md b/os/yocto/layers/meta-nvidia/README.md new file mode 100644 index 000000000..aaf3f25d2 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/README.md @@ -0,0 +1,138 @@ +# Overview + +The meta-nvidia layer provides support for NVIDIA graphics drivers and related components for Yocto Project-based distributions. This layer includes recipes for building the NVIDIA binary graphics driver, the GL Vendor-Neutral Dispatch library (libglvnd), and modifications to the mesa package to ensure compatibility with NVIDIA's proprietary drivers. + +![loaded-modules](artwork/loaded_modules.png) +# Contents + +- Configuration: The layer.conf file contains the necessary configurations for the layer, including BBPATH, BBFILES, and other essential settings. +- Custom Licenses: The custom-licenses directory contains custom license files that may be required by the recipes in this layer. +- Recipes: + - libglvnd: Provides the GL Vendor-Neutral Dispatch library. + - mesa: Contains modifications to the mesa package to ensure compatibility with NVIDIA's proprietary drivers. + - nvidia: Contains recipes for building NVIDIA's binary graphics driver and related components. + +# Key Features + +- libglvnd: The GL Vendor-Neutral Dispatch library allows multiple OpenGL implementations to coexist on the same system. +- NVIDIA Binary Graphics Driver: Provides support for NVIDIA GPUs, enabling hardware-accelerated graphics, CUDA support, and other NVIDIA-specific features. +- Mesa Modifications: Ensures that the open-source Mesa graphics library can coexist with NVIDIA's proprietary drivers. + +# Usage + +To use the meta-nvidia layer in your Yocto Project build: + +- Clone the meta-nvidia repository to your local machine. +- Add the path to the meta-nvidia layer to your bblayers.conf file. +- Include the desired recipes in your image or build them individually using bitbake. + +To use this layer, include it in your bblayers.conf and add the +following to your BSP, distro, or local config: + +```bash +DISTRO_FEATURES:append = " x11 opengl" +DISTRO_FEATURES:remove = " wayland" +IMAGE_INSTALL:append = " libxshmfence cmake" +IMAGE_INSTALL:append = " packagegroup-core-buildessential" +IMAGE_INSTALL:append = " acpid" +IMAGE_INSTALL:append = " nvidia" +PREFERRED_PROVIDER_virtual/libgl = "libglvnd" +PREFERRED_PROVIDER_virtual/libgles1 = "libglvnd" +PREFERRED_PROVIDER_virtual/libgles3 = "libglvnd" +PREFERRED_PROVIDER_virtual/egl = "libglvnd" +PREFERRED_PROVIDER_virtual/libgl-native = "mesa-native" +PREFERRED_PROVIDER_virtual/nativesdk-libgl = "nativesdk-mesa-gl" +PREFERRED_PROVIDER_virtual/mesa = "libglvnd" +KERNEL_MODULE_AUTOLOAD:append = " nvidia nvidia-drm nvidia-modeset nvidia-uvm" +XSERVER = " \ + ${XSERVER_X86_BASE} \ + ${XSERVER_X86_EXT} \ + ${XSERVER_X86_MODESETTING} \ + nvidia" +``` + +# Testing nvidia-container-toolkit and GPU Workloads + +- For testing nvidia-container-toolkit inside the container execute the following commands: + +```bash +sudo ctr images pull docker.io/nvidia/cuda:12.0.0-base-ubuntu20.04 + +sudo ctr run --rm --gpus 0 --runtime io.containerd.runc.v1 --privileged docker.io/nvidia/cuda:12.0.0-base-ubuntu20.04 nvidia-smi nvidia-smi +``` + +- For testing nvidia-container-toolkit with k3s + +```bash +cat <&1 | grep -q "clang version" && echo clang || echo gcc) + CFLAGS := -std=gnu11 -O2 -g -fdata-sections -ffunction-sections -fplan9-extensions -fstack-protector -fno-strict-aliasing -fvisibility=hidden \ + -Wall -Wextra -Wcast-align -Wpointer-arith -Wmissing-prototypes -Wnonnull \ + -Wwrite-strings -Wlogical-op -Wformat=2 -Wmissing-format-attribute -Winit-self -Wshadow \ + -Wstrict-prototypes -Wunreachable-code -Wconversion -Wsign-conversion \ +- -Wno-unknown-warning-option -Wno-format-extra-args -Wno-gnu-alignof-expression $(CFLAGS) ++ -Wno-format-extra-args $(if $(filter clang,$(CCNAME)),-Wno-unknown-warning-option -Wno-gnu-alignof-expression,) \ ++ -I=/usr/include/tirpc $(CFLAGS) + LDFLAGS := -Wl,-zrelro -Wl,-znow -Wl,-zdefs -Wl,--gc-sections $(LDFLAGS) + LDLIBS := $(LDLIBS) ++CFLAGS := -Wno-error $(CFLAGS) ++LDLIBS := -Wno-error $(LDLIBS) ++CPPFLAGS := -Wno-error $(CPPFLAGS) + + # Library flags (recursively expanded to handle target-specific flags) + LIB_CPPFLAGS = -DNV_LINUX -isystem $(DEPS_DIR)$(includedir) -include $(BUILD_DEFS) + LIB_CFLAGS = -fPIC +-LIB_LDFLAGS = -L$(DEPS_DIR)$(libdir) -shared -Wl,-soname=$(LIB_SONAME) +-LIB_LDLIBS_STATIC = -l:libnvidia-modprobe-utils.a +-LIB_LDLIBS_SHARED = -ldl -lcap ++LIB_LDFLAGS = -shared -Wl,-soname=$(LIB_SONAME) ++LIB_LDLIBS_STATIC = -L$(DEPS_DIR)$(libdir) -l:libnvidia-modprobe-utils.a -L=$(libdir) -l:libtirpc.a ++LIB_LDLIBS_SHARED = -ldl -lcap -lpthread + ifeq ($(WITH_NVCGO), yes) + LIB_CPPFLAGS += -DWITH_NVCGO + LIB_LDLIBS_SHARED += -lpthread +@@ -205,7 +210,11 @@ $(BUILD_DEFS): + @printf '#define BUILD_FLAGS "%s"\n' '$(strip $(CPPFLAGS) $(CFLAGS) $(LDFLAGS))' >>$(BUILD_DEFS) + @printf '#define BUILD_REVISION "%s"\n' '$(strip $(REVISION))' >>$(BUILD_DEFS) + @printf '#define BUILD_PLATFORM "%s"\n' '$(strip $(PLATFORM))' >>$(BUILD_DEFS) +- ++ifeq ($(EXCLUDE_BUILD_FLAGS),) ++ @printf '#define BUILD_FLAGS "%s"\n' '$(strip $(CPPFLAGS) $(CFLAGS) $(LDFLAGS))' >>$(BUILD_DEFS) ++else ++ @printf '#define BUILD_FLAGS ""\n' >>$(BUILD_DEFS) ++endif + $(LIB_RPC_SRCS): $(LIB_RPC_SPEC) + $(RM) $@ + cd $(dir $@) && $(RPCGEN) $(RPCGENFLAGS) -C -M -N -o $(notdir $@) $(LIB_RPC_SPEC) +@@ -224,17 +233,14 @@ $(LIB_SHARED): $(LIB_OBJS) + $(OBJCPY) --only-keep-debug $@ $(LIB_SONAME) + $(OBJCPY) --add-gnu-debuglink=$(LIB_SONAME) $@ + $(MV) $(LIB_SONAME) $(DEBUG_DIR) +- $(STRIP) --strip-unneeded -R .comment $@ + + $(LIB_STATIC_OBJ): $(LIB_OBJS) + # FIXME Handle user-defined LDFLAGS and LDLIBS + $(LD) -d -r --exclude-libs ALL -L$(DEPS_DIR)$(libdir) $(OUTPUT_OPTION) $^ $(LIB_LDLIBS_STATIC) + $(OBJCPY) --localize-hidden $@ +- $(STRIP) --strip-unneeded -R .comment $@ + + $(BIN_NAME): $(BIN_OBJS) + $(CC) $(BIN_CFLAGS) $(BIN_CPPFLAGS) $(BIN_LDFLAGS) $(OUTPUT_OPTION) $^ $(BIN_SCRIPT) $(BIN_LDLIBS) +- $(STRIP) --strip-unneeded -R .comment $@ + + ##### Public rules ##### + +@@ -244,7 +250,6 @@ all: shared static tools + # Run with ASAN_OPTIONS="protect_shadow_gap=0" to avoid CUDA OOM errors + debug: CFLAGS += -pedantic -fsanitize=undefined -fno-omit-frame-pointer -fno-common -fsanitize=address + debug: LDLIBS += -lubsan +-debug: STRIP := @echo skipping: strip + debug: shared static tools + + tools: $(BIN_NAME) +diff --git a/mk/common.mk b/mk/common.mk +index f6d5fbf2..d46584b7 100644 +--- a/mk/common.mk ++++ b/mk/common.mk +@@ -28,7 +28,7 @@ else + DATE := $(shell date -u --iso-8601=minutes) + endif + REVISION ?= $(shell git rev-parse HEAD) +-COMPILER := $(realpath $(shell which $(CC))) ++COMPILER := $(realpath $(shell which $(firstword $(CC)))) + PLATFORM ?= $(shell uname -m) + + ifeq ($(DATE),) diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container/0002-secomp-fix.patch b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container/0002-secomp-fix.patch new file mode 100644 index 000000000..1a3c46566 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container/0002-secomp-fix.patch @@ -0,0 +1,15 @@ +Upstream-Status: Inappropriate [embedded specific] + +diff --git a/Makefile b/Makefile +index a374cc09..b6632580 100644 +--- a/Makefile ++++ b/Makefile +@@ -20,7 +20,7 @@ + WITH_NVCGO ?= yes + WITH_LIBELF ?= no + WITH_TIRPC ?= no +-WITH_SECCOMP ?= yes ++WITH_SECCOMP := yes + + ##### Global definitions ##### + diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container/0003-fix-remove-buildpath-for-package-qa.patch b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container/0003-fix-remove-buildpath-for-package-qa.patch new file mode 100644 index 000000000..ec42da634 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container/0003-fix-remove-buildpath-for-package-qa.patch @@ -0,0 +1,26 @@ +Upstream-Status: Inappropriate [embedded specific] + +From 13cc971cf7ab0bb8fe8528f2ebd65685a81b7ae3 Mon Sep 17 00:00:00 2001 +From: Atharva Nandanwar +Date: Fri, 19 Aug 2022 15:25:18 -0600 +Subject: [PATCH] fix: remove buildpath for package qa + +* get EXTRA_CFLAGS for nvidia-modprobe +--- + mk/nvidia-modprobe.mk | 1 ++ + 1 file changed, 1 insertions(+) + +diff --git a/mk/nvidia-modprobe.mk b/mk/nvidia-modprobe.mk +index 3406222..c7af214 100644 +--- a/mk/nvidia-modprobe.mk ++++ b/mk/nvidia-modprobe.mk +@@ -26,6 +26,7 @@ PATCH_FILE := $(MAKE_DIR)/nvidia-modprobe.patch + ARFLAGS := -rU + CPPFLAGS := -D_FORTIFY_SOURCE=2 -DNV_LINUX + CFLAGS := -O2 -g -fdata-sections -ffunction-sections -fstack-protector -fno-strict-aliasing -fPIC ++CFLAGS += $(NVIDIA_MODPROBE_EXTRA_CFLAGS) + + ##### Private rules ##### + +-- +2.34.1 diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container_1.00.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container_1.00.bb new file mode 100644 index 000000000..4e4e341df --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libnvidia-container_1.00.bb @@ -0,0 +1,78 @@ +require libnvidia-container.inc +inherit features_check pkgconfig + +SUMMARY = "libNVIDIA Container for Yocto" + +fakeroot do_unpack_modprobe() { + mkdir -p ${S}/deps/src + if [ -d "${UNPACKDIR}/nvidia-modprobe" ]; then + mv ${UNPACKDIR}/nvidia-modprobe ${S}/deps/src/nvidia-modprobe-${NVIDIA_MODPROBE_VERSION} + fi +} +addtask unpack_modprobe after do_unpack before do_patch +do_unpack_modprobe[dirs] = "${S}" +do_unpack_modprobe[vardeps] += "NVIDIA_MODPROBE_VERSION" +# Ensure pseudo (fakeroot worker) is staged before this fakeroot task runs. +do_unpack_modprobe[depends] += "pseudo-native:do_populate_sysroot" + +PACKAGECONFIG ??= "seccomp" +PACKAGECONFIG[seccomp] = "WITH_SECCOMP=yes,WITH_SECCOMP=no,libseccomp" + +# We need to link with libelf, otherwise we need to +# include bmake-native which does not exist at the moment. +EXTRA_OEMAKE = "EXCLUDE_BUILD_FLAGS=1 PLATFORM=${HOST_ARCH} WITH_NVCGO=yes WITH_LIBELF=yes COMPILER=${@d.getVar('CC').split()[0]} REVISION=${SRCREV_libnvidia} ${PACKAGECONFIG_CONFARGS} \ + NVIDIA_MODPROBE_EXTRA_CFLAGS=${NVIDIA_MODPROBE_EXTRA_CFLAGS} \ + GOCACHE=${WORKDIR}/go-cache GOPATH=${WORKDIR}/go GOMODCACHE=${WORKDIR}/go-mod" +NVIDIA_MODPROBE_EXTRA_CFLAGS ?= "-ffile-prefix-map=${WORKDIR}=/usr/src/debug/${PN}/${EXTENDPE}${PV}-${PR}" +CFLAGS:prepend = " -I${RECIPE_SYSROOT_NATIVE}/usr/include/tirpc " + +export OBJCPY = "${OBJCOPY}" +GO_IMPORT = "github.com/NVIDIA/nvidia-container-toolkit" +SECURITY_LDFLAGS = "" +LDFLAGS += "-Wl,-z,lazy" +GO_LINKSHARED = "" +REQUIRED_DISTRO_FEATURES = "virtualization" +do_configure:append() { + # Mark Nvidia modprobe as downloaded + touch ${S}/deps/src/nvidia-modprobe-${NVIDIA_MODPROBE_VERSION}/.download_stamp +} + +do_compile() { + # Ensure the copied bmake is used during the build + export PATH=${WORKDIR}:$PATH + + #go fix + export GOPATH="${WORKDIR}/go" + export GOCACHE="${WORKDIR}/go-cache" + export GOMODCACHE="${WORKDIR}/go-mod" + mkdir -p ${GOPATH} ${GOCACHE} ${GOMODCACHE} + + # Ensure reproducible builds + export SOURCE_DATE_EPOCH="${@d.getVar('SOURCE_DATE_EPOCH') or '0'}" + export CGO_LDFLAGS="${CGO_LDFLAGS} -Wl,--build-id=none" + + # Point curl to the correct CA certificates in the native sysroot + export CURL_CA_BUNDLE="${RECIPE_SYSROOT_NATIVE}/etc/ssl/certs/ca-certificates.crt" + + oe_runmake +} + +do_install() { + oe_runmake install DESTDIR=${D} + install -d ${D}${sysconfdir}/nvidia-container-runtime + # install -m 0644 ${S}/src/${GO_IMPORT}/config/config.toml.ubuntu ${D}${sysconfdir}/nvidia-container-runtime/config.toml + # sed -i -e's,ldconfig\.real,ldconfig,' ${D}${sysconfdir}/nvidia-container-runtime/config.toml + # sed -i -e's,mode = "auto",mode = "legacy",' ${D}${sysconfdir}/nvidia-container-runtime/config.toml + ln -sf nvidia-container-runtime-hook ${D}${bindir}/nvidia-container-toolkit +} + +FILES_${PN} += "/usr/local/bin /usr/local/lib" + +RDEPENDS:${PN}:append = " ldconfig" + +do_compile[network] = "1" + +# Added to skip buildpath QA errors for files generated by rpcgen +INSANE_SKIP:${PN}-src = "buildpaths" +# Skip QA checks for Go shared library +INSANE_SKIP:${PN} = "already-stripped ldflags buildpaths" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libtirpc134_1.3.4.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libtirpc134_1.3.4.bb new file mode 100644 index 000000000..f4d1cacc3 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/libnvidia-container/libtirpc134_1.3.4.bb @@ -0,0 +1,31 @@ +SUMMARY = "Transport-Independent RPC library" +DESCRIPTION = "Libtirpc is a port of Suns Transport-Independent RPC library to Linux" +SECTION = "libs/network" +HOMEPAGE = "http://sourceforge.net/projects/libtirpc/" +BUGTRACKER = "http://sourceforge.net/tracker/?group_id=183075&atid=903784" +LICENSE = "BSD-3-Clause" +LIC_FILES_CHKSUM = "file://COPYING;md5=f835cce8852481e4b2bbbdd23b5e47f3 \ + file://src/netname.c;beginline=1;endline=27;md5=f8a8cd2cb25ac5aa16767364fb0e3c24" + +SRC_URI = "${SOURCEFORGE_MIRROR}/libtirpc/libtirpc-${PV}.tar.bz2" +SRC_URI[sha256sum] = "1e0b0c7231c5fa122e06c0609a76723664d068b0dba3b8219b63e6340b347860" + +# SRC_URI += "file://0001-__rpc_dtbsize-rlim_cur-instead-of-rlim_max.patch" + +S = "${UNPACKDIR}/libtirpc-${PV}" + +inherit autotools pkgconfig + +DISABLE_STATIC = "" +EXTRA_OECONF = "--disable-gssapi --enable-static" + +# Append -fPIC to CFLAGS and fix GCC 15 compatibility +CFLAGS:append = " -fPIC -Wno-error=incompatible-pointer-types -Wno-error=int-conversion -std=gnu17" + +do_install:append() { + rm -r ${D}${sysconfdir} ${D}${datadir} ${D}${libdir}/pkgconfig + rm ${D}${libdir}/*.so* + cp -r ${D}${includedir}/tirpc ${D}${includedir}/tirpc-1.3.4 +} + +inherit nopackages diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvattest/files/10-nvidia-gpu-ordering.conf b/os/yocto/layers/meta-nvidia/recipes-graphics/nvattest/files/10-nvidia-gpu-ordering.conf new file mode 100644 index 000000000..50e0b7d1f --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvattest/files/10-nvidia-gpu-ordering.conf @@ -0,0 +1,8 @@ +# Ordering only, not a hard dependency: on GPU instances, `dstack-util setup` +# (run by dstack-prepare.service) performs local GPU TEE attestation per the +# app-compose `requirements.verify_gpu` policy, which needs the nvidia driver +# loaded (nvidia-persistenced) and the NVLink fabric up (nvidia-fabricmanager). +# Both units skip cleanly on GPU-less instances via nvidia-gpu-detect. +[Unit] +Wants=nvidia-persistenced.service nvidia-fabricmanager.service +After=nvidia-persistenced.service nvidia-fabricmanager.service diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvattest/nvattest_2026.06.09.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvattest/nvattest_2026.06.09.bb new file mode 100644 index 000000000..7d907d69d --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvattest/nvattest_2026.06.09.bb @@ -0,0 +1,113 @@ +SUMMARY = "NVIDIA GPU attestation CLI" +DESCRIPTION = "Builds NVIDIA's nvattest CLI. dstack-util setup runs it at boot to gate readiness on local GPU TEE attestation (app-compose requirements.verify_gpu)." +HOMEPAGE = "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/NVIDIA/attestation-sdk" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e620fc90e76c4aa0c3efdd1673ca0b3b" + +SRC_URI = " \ + git://github.com/NVIDIA/attestation-sdk.git;protocol=https;branch=main \ + file://10-nvidia-gpu-ordering.conf \ +" +SRCREV = "9d12801cea8a198ea0f29640dfaf8a4017c841c5" + +OECMAKE_SOURCEPATH = "${S}/nv-attestation-cli" + +inherit cmake pkgconfig + +# The SDK embeds the regorus Rego policy engine (Rust, via Corrosion), so a +# Rust toolchain is required. Use the prebuilt toolchain from meta-rust-bin +# (cargo-bin-native pulls rust-bin-cross-${TARGET_ARCH}), the same toolchain +# dstack-guest builds with -- NOT oe-core's rust-native (bootstraps rustc from +# source, very expensive). +DEPENDS += " \ + cargo-bin-native \ + curl \ + openssl \ + libxml2 \ + xmlsec1 \ + spdlog \ + nlohmann-json \ + nvidia \ +" + +RDEPENDS:${PN} += " \ + ca-certificates \ + nvidia \ + nvidia-fabricmanager \ + nvidia-persistenced \ +" + +EXTRA_OECMAKE += " \ + -DBUILD_TESTING=OFF \ + -DNVAT_BUILD_TESTS=OFF \ + -DNVAT_BUILD_SAMPLES=OFF \ + -DCMAKE_SKIP_RPATH=ON \ + -DFETCHCONTENT_FULLY_DISCONNECTED=OFF \ + -DUSE_SYSTEM_DEPS=ON \ +" + +# Keep cargo state inside the workdir (Corrosion invokes cargo for regorus). +export CARGO_HOME = "${WORKDIR}/cargo_home" +export RUST_BACKTRACE = "1" + +# rustc does not inherit the C toolchain's -ffile-prefix-map, so the regorus +# static lib embedded in libnvat would otherwise carry TMPDIR paths +# ([buildpaths] QA / reproducibility issue). +export RUSTFLAGS = "--remap-path-prefix=${WORKDIR}=/usr/src/debug/${PN}/${PV}" + +# Corrosion invokes cargo with host triple == target triple +# (x86_64-unknown-linux-gnu). Cargo would then link host-side build scripts +# with the cross gcc but without the target sysroot (cannot find Scrt1.o/-lc). +# Split host vs target linker config the same way meta-rust-bin's cargo_bin +# class does: build scripts link with the native toolchain. +do_compile:prepend() { + mkdir -p ${WORKDIR}/wrappers + echo "#!/bin/sh" > ${WORKDIR}/wrappers/linker-native-wrapper.sh + echo "${BUILD_CC} ${BUILD_LDFLAGS} \"\$@\"" >> ${WORKDIR}/wrappers/linker-native-wrapper.sh + chmod +x ${WORKDIR}/wrappers/linker-native-wrapper.sh + + export __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS="nightly" + export CARGO_UNSTABLE_TARGET_APPLIES_TO_HOST="true" + export CARGO_UNSTABLE_HOST_CONFIG="true" + export CARGO_TARGET_APPLIES_TO_HOST="false" + export CARGO_HOST_LINKER="${WORKDIR}/wrappers/linker-native-wrapper.sh" +} + +# Network is needed at configure/compile time because: +# - nv-attestation-cli CMake FetchContent: CLI11, nlohmann-json (+ fmt/spdlog +# headers) +# - nv-attestation-sdk-cpp CMake FetchContent: Corrosion, regorus, jwt-cpp +# - Corrosion runs cargo, which fetches the regorus-ffi crate dependencies +# All refs are pinned (git tags/commits) upstream. TODO: vendor these via +# SRC_URI + cargo vendor for a fully offline, reproducible fetch. +do_configure[network] = "1" +do_compile[network] = "1" + +do_install() { + DESTDIR=${D} cmake --install ${B} --prefix ${prefix} + + if [ ! -x ${D}${bindir}/nvattest ]; then + bbfatal "nvattest binary was not produced by the build" + fi + + # cargo/Corrosion leaves host-side proc-macro dylibs in the build tree; + # make sure none of them ever end up in the image (only libnvat is a + # real target library). + find ${D}${libdir} -maxdepth 1 -name 'lib*.so*' ! -name 'libnvat.so*' -delete + + # Make dstack-prepare (which runs the attestation) start after the nvidia + # userspace services it depends on. + install -d ${D}${systemd_system_unitdir}/dstack-prepare.service.d + install -m 0644 ${UNPACKDIR}/10-nvidia-gpu-ordering.conf \ + ${D}${systemd_system_unitdir}/dstack-prepare.service.d/10-nvidia-gpu-ordering.conf +} + +FILES:${PN} += " \ + ${systemd_system_unitdir}/dstack-prepare.service.d/10-nvidia-gpu-ordering.conf \ + ${libdir}/lib*.so \ + ${libdir}/lib*.so.* \ +" +FILES_SOLIBSDEV = "" +FILES:${PN}-dev:remove = "${libdir}/lib*.so" + +INSANE_SKIP:${PN} += "dev-so already-stripped" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/files/0001-Fix-cgo-LDFLAGS-for-go-1.21-and-later.patch b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/files/0001-Fix-cgo-LDFLAGS-for-go-1.21-and-later.patch new file mode 100644 index 000000000..124258b9a --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/files/0001-Fix-cgo-LDFLAGS-for-go-1.21-and-later.patch @@ -0,0 +1,27 @@ +From 00529c2b30cf74993bb124ba82a6947703540584 Mon Sep 17 00:00:00 2001 +From: Matt Madison +Date: Sun, 17 Mar 2024 09:33:12 -0700 +Subject: [PATCH] Fix cgo LDFLAGS for go 1.21 and later + +Upstream-Status: Pending + +Signed-off-by: Matt Madison +--- + internal/cuda/cuda.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/internal/cuda/cuda.go b/internal/cuda/cuda.go +index 2c70a82..3fc7952 100644 +--- a/internal/cuda/cuda.go ++++ b/internal/cuda/cuda.go +@@ -23,7 +23,7 @@ import ( + ) + + /* +-#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files ++#cgo LDFLAGS: -Wl,--export-dynamic -Wl,--unresolved-symbols=ignore-in-object-files + + #ifdef _WIN32 + #define CUDAAPI __stdcall +-- +2.34.1 diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/files/config.toml b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/files/config.toml new file mode 100644 index 000000000..e09dec2a3 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/files/config.toml @@ -0,0 +1,26 @@ +[nvidia-container-cli] +#root = "/run/nvidia/driver" +#path = "/usr/bin/nvidia-container-cli" +environment = [] +# debug = "/var/log/nvidia-container-toolkit.log" +#ldcache = "/etc/ld.so.cache" +load-kmods = true +no-cgroups = false +#user = "root:video" +ldconfig = "@/sbin/ldconfig" +#alpha-merge-visible-devices-envvars = false + +[nvidia-container-runtime] +# debug = "/var/log/nvidia-container-runtime.log" +log-level = "debug" + +# Specify the runtimes to consider. This list is processed in order and the PATH +# searched for matching executables unless the entry is an absolute path. +runtimes = [ + "runc", +] + +mode = "auto" + +[nvidia-container-runtime.modes.csv] +mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/nvidia-container-toolkit.inc b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/nvidia-container-toolkit.inc new file mode 100644 index 000000000..200471374 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/nvidia-container-toolkit.inc @@ -0,0 +1,15 @@ +DESCRIPTION = "NVIDIA Container Toolkit" +HOMEPAGE = "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/NVIDIA/nvidia-container-toolkit" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://src/${GO_IMPORT}/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" + +SRC_URI = "git://github.com/NVIDIA/nvidia-container-toolkit.git;protocol=https;branch=main;destsuffix=${BP}/src/${GO_IMPORT}" +SRCREV = "c748619c592030519361274a2fdd43f2c2ced73a" + +SRC_URI += "file://config.toml" +GO_IMPORT = "github.com/NVIDIA/nvidia-container-toolkit" + +DEPENDS = " \ + curl-native ca-certificates-native go-native \ + coreutils-native \ + " diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/nvidia-container-toolkit_1.00.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/nvidia-container-toolkit_1.00.bb new file mode 100644 index 000000000..44660559f --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia-container-toolkit/nvidia-container-toolkit_1.00.bb @@ -0,0 +1,54 @@ +require nvidia-container-toolkit.inc + +SUMMARY = "NVIDIA Container Toolkit for Yocto" + +GO_INSTALL = "${GO_IMPORT}/cmd/..." +# The go-nvml symbol lookup functions *require* lazy dynamic symbol resolution +SECURITY_LDFLAGS = "" +LDFLAGS += "-Wl,-z,lazy" +GO_LINKSHARED = "" + +REQUIRED_DISTRO_FEATURES = "virtualization" + +inherit go go-mod features_check + +do_compile() { + echo "Current directory: $(pwd)" + ls -alh + oe_runmake -C ${S}/src/${GO_IMPORT} cmds + +} + +install_bin_stripped() { + install -m 0755 ${S}/src/${GO_IMPORT}/$1 ${D}${bindir}/$1 + ${STRIP} --remove-section=.note.gnu.build-id ${D}${bindir}/$1 + ${STRIP} --remove-section=.note.go.buildid ${D}${bindir}/$1 +} + +do_install() { + # Create the target directories in the image file system + install -d ${D}${bindir} + + # Copy each binary to the target directory + install_bin_stripped nvidia-container-runtime + install_bin_stripped nvidia-container-runtime.cdi + install_bin_stripped nvidia-container-runtime-hook + install_bin_stripped nvidia-container-runtime.legacy + install_bin_stripped nvidia-ctk + + ln -sf nvidia-container-runtime-hook ${D}${bindir}/nvidia-container-toolkit + + # create config.toml + # Ensure the installation directory exists + install -d ${D}/etc/nvidia-container-runtime + # Install the config.toml file + install -m 0644 ${UNPACKDIR}/config.toml ${D}/etc/nvidia-container-runtime/config.toml +} + +INSANE_SKIP:${PN} += "already-stripped buildpaths textrel" +INSANE_SKIP:${PN}:append = "already-stripped buildpaths textrel" +FILES_${PN} += "/usr/local/*" + +RDEPENDS:${PN} = "\ + libnvidia-container \ + " \ No newline at end of file diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-fabricmanager-nvswitch-condition.conf b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-fabricmanager-nvswitch-condition.conf new file mode 100644 index 000000000..a55d2d272 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-fabricmanager-nvswitch-condition.conf @@ -0,0 +1,12 @@ +# Skip the fabric manager cleanly on hosts without NVSwitch (i.e. any non +# HGX/DGX instance, including GPU instances that have no NVSwitch) instead of +# letting it fail. ExecCondition exit 1 -> systemd marks the unit as skipped, +# not failed. +[Service] +ExecCondition=/usr/bin/nvidia-gpu-detect nvswitch +# Load NVIDIA modules only after NVSwitch detection succeeds to avoid boot-time +# modprobe failures/noise on GPU-less hosts. +ExecStartPre=/usr/bin/env modprobe nvidia +ExecStartPre=/usr/bin/env modprobe nvidia-modeset +ExecStartPre=/usr/bin/env modprobe nvidia-drm +ExecStartPre=/usr/bin/env modprobe nvidia-uvm diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-gpu-detect b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-gpu-detect new file mode 100755 index 000000000..8547c949a --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-gpu-detect @@ -0,0 +1,52 @@ +#!/bin/sh +# nvidia-gpu-detect - report NVIDIA GPU / NVSwitch presence via sysfs PCI. +# +# Intended for use as a systemd ExecCondition= so that GPU-only services skip +# cleanly (instead of failing) on instances without a GPU / without NVSwitch. +# This lets the NVIDIA and plain images be merged into a single image. +# +# exit 0 -> hardware present (systemd runs the unit) +# exit 1 -> hardware absent (systemd skips the unit, no failure) +# +# Detection mirrors +# tools/vm-runner/enable-vfio-passthrough.sh, but reads PCI class codes +# from sysfs so it works before the nvidia driver is loaded and without relying +# on lspci / the pci.ids name database: +# +# GPU = NVIDIA (0x10de) VGA (0x0300xx) or 3D controller (0x0302xx) +# NVSwitch = NVIDIA (0x10de) bridge (0x06xxxx, any bridge subclass) +# +# NVSwitch matches any NVIDIA bridge-class device (mirrors the `grep 'Bridge'` +# heuristic in tools/vm-runner/enable-vfio-passthrough.sh) rather than +# only the "Other bridge" subclass 0x0680, so it stays correct if a future +# NVSwitch enumerates under a different bridge subclass. This is safe here +# because these images run as TDX guests whose only NVIDIA-vendor devices are +# GPUs (class 0x03xx) and NVSwitches (class 0x06xx). + +NVIDIA_VENDOR="0x10de" + +# match_vendor_class : succeed if any PCI device has NVIDIA's +# vendor id and a class matching the given glob (e.g. "0x0302*"). +match_vendor_class() { + for dev in /sys/bus/pci/devices/*; do + [ -r "$dev/vendor" ] && [ -r "$dev/class" ] || continue + [ "$(cat "$dev/vendor")" = "$NVIDIA_VENDOR" ] || continue + case "$(cat "$dev/class")" in + $1) return 0 ;; + esac + done + return 1 +} + +case "${1:-gpu}" in + gpu) + match_vendor_class '0x0300*' || match_vendor_class '0x0302*' + ;; + nvswitch) + match_vendor_class '0x06*' + ;; + *) + echo "usage: ${0##*/} {gpu|nvswitch}" >&2 + exit 64 + ;; +esac diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-persistenced.service b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-persistenced.service new file mode 100644 index 000000000..f40aef461 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia-persistenced.service @@ -0,0 +1,26 @@ +[Unit] +Description=NVIDIA Persistence Daemon +Wants=syslog.target +Before=app-compose.service docker.service +After=systemd-modules-load.service modprobe@.service + +[Service] +Type=oneshot +# Skip cleanly on instances without an NVIDIA GPU (exit 1 -> unit skipped, not +# failed), so the NVIDIA image can run on GPU-less hosts. +ExecCondition=/usr/bin/nvidia-gpu-detect gpu +# Load NVIDIA modules only after GPU detection succeeds to avoid boot-time +# modprobe failures/noise on GPU-less hosts. +ExecStartPre=/usr/bin/env modprobe nvidia +ExecStartPre=/usr/bin/env modprobe nvidia-modeset +ExecStartPre=/usr/bin/env modprobe nvidia-drm +ExecStartPre=/usr/bin/env modprobe nvidia-uvm +ExecStart=/usr/bin/env nvidia-persistenced --uvm-persistence-mode +# NOTE: the GPU ready state (conf-compute -srs 1) is intentionally NOT set +# here. It is set by `dstack-util setup` (dstack-prepare.service), only after +# local GPU TEE attestation via nvattest succeeds -- unless the deployment +# opts out with app-compose requirements.verify_gpu=false. +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia.conf b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia.conf new file mode 100644 index 000000000..e0b90c5d4 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/files/nvidia.conf @@ -0,0 +1 @@ +options nvidia NVreg_RegistryDwords="RmEnableProtectedPcie=0x1" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_580.105.08.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_580.105.08.bb new file mode 100644 index 000000000..a6ffc4314 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_580.105.08.bb @@ -0,0 +1,33 @@ +SUMMARY = "NVIDIA NSCQ library" +DESCRIPTION = "NVIDIA NSCQ (NVIDIA System Communication Queue) library for NVIDIA GPU systems" +HOMEPAGE = "https://developer.nvidia.com/" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2cc00be68c1227a7c42ff3620ef75d05" + +SRC_URI = "https://developer.download.nvidia.cn/compute/nvidia-driver/redist/libnvidia_nscq/linux-x86_64/libnvidia_nscq-linux-x86_64-${PV}-archive.tar.xz" +SRC_URI[sha256sum] = "71086dc7d9c97ac20a7d14de2a119e3b2ee97a08da452d63c2c4f5a71d80e19a" + +S = "${UNPACKDIR}/libnvidia_nscq-linux-x86_64-${PV}-archive" + +INSANE_SKIP:${PN} = "already-stripped ldflags" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +do_install() { + # Create directories + install -d ${D}${libdir} + + # Install libraries + install -m 0755 ${S}/lib/libnvidia-nscq.so.${PV} ${D}${libdir} + ln -sf libnvidia-nscq.so.${PV} ${D}${libdir}/libnvidia-nscq.so.2.0 + ln -sf libnvidia-nscq.so.2.0 ${D}${libdir}/libnvidia-nscq.so.2 + ln -sf libnvidia-nscq.so.2 ${D}${libdir}/libnvidia-nscq.so +} + +FILES:${PN} = "\ + ${libdir}/libnvidia-nscq.so.${PV} \ + ${libdir}/libnvidia-nscq.so.2.0 \ + ${libdir}/libnvidia-nscq.so.2 \ + ${libdir}/libnvidia-nscq.so \ +" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_580.95.05.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_580.95.05.bb new file mode 100644 index 000000000..5cca02880 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_580.95.05.bb @@ -0,0 +1,32 @@ +SUMMARY = "NVIDIA NSCQ library" +DESCRIPTION = "NVIDIA NSCQ (NVIDIA System Communication Queue) library for NVIDIA GPU systems" +HOMEPAGE = "https://developer.nvidia.com/" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2cc00be68c1227a7c42ff3620ef75d05" + +SRC_URI = "https://developer.download.nvidia.cn/compute/nvidia-driver/redist/libnvidia_nscq/linux-x86_64/libnvidia_nscq-linux-x86_64-${PV}-archive.tar.xz" +SRC_URI[md5sum] = "6bc20061ebdae98fadd7a76110b44430" +SRC_URI[sha256sum] = "c2285c12f10ec2afc0ad2949f7fcc282b6fd37f32165c1df241451ccabb1067a" + +S = "${UNPACKDIR}/libnvidia_nscq-linux-x86_64-${PV}-archive" + +INSANE_SKIP:${PN} = "already-stripped ldflags" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +do_install() { + install -d ${D}${libdir} + + install -m 0755 ${S}/lib/libnvidia-nscq.so.${PV} ${D}${libdir} + ln -sf libnvidia-nscq.so.${PV} ${D}${libdir}/libnvidia-nscq.so.2.0 + ln -sf libnvidia-nscq.so.2.0 ${D}${libdir}/libnvidia-nscq.so.2 + ln -sf libnvidia-nscq.so.2 ${D}${libdir}/libnvidia-nscq.so +} + +FILES:${PN} = "\ + ${libdir}/libnvidia-nscq.so.${PV} \ + ${libdir}/libnvidia-nscq.so.2.0 \ + ${libdir}/libnvidia-nscq.so.2 \ + ${libdir}/libnvidia-nscq.so \ +" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_595.58.03.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_595.58.03.bb new file mode 100644 index 000000000..26584ed61 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/libnvidia-nscq_595.58.03.bb @@ -0,0 +1,32 @@ +SUMMARY = "NVIDIA NSCQ library" +DESCRIPTION = "NVIDIA NSCQ (NVIDIA System Communication Queue) library for NVIDIA GPU systems" +HOMEPAGE = "https://developer.nvidia.com/" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2cc00be68c1227a7c42ff3620ef75d05" + +SRC_URI = "https://developer.download.nvidia.cn/compute/nvidia-driver/redist/libnvidia_nscq/linux-x86_64/libnvidia_nscq-linux-x86_64-${PV}-archive.tar.xz" +SRC_URI[md5sum] = "5b559a614f2c2c79b6eb796b68b6fb0f" +SRC_URI[sha256sum] = "1c008787111fc55a7fc1d3521a6d41c2523b8949354e5651eadb4498554e5469" + +S = "${UNPACKDIR}/libnvidia_nscq-linux-x86_64-${PV}-archive" + +INSANE_SKIP:${PN} = "already-stripped ldflags" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +do_install() { + install -d ${D}${libdir} + + install -m 0755 ${S}/lib/libnvidia-nscq.so.${PV} ${D}${libdir} + ln -sf libnvidia-nscq.so.${PV} ${D}${libdir}/libnvidia-nscq.so.2.0 + ln -sf libnvidia-nscq.so.2.0 ${D}${libdir}/libnvidia-nscq.so.2 + ln -sf libnvidia-nscq.so.2 ${D}${libdir}/libnvidia-nscq.so +} + +FILES:${PN} = "\ + ${libdir}/libnvidia-nscq.so.${PV} \ + ${libdir}/libnvidia-nscq.so.2.0 \ + ${libdir}/libnvidia-nscq.so.2 \ + ${libdir}/libnvidia-nscq.so \ +" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_%.bbappend b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_%.bbappend new file mode 100644 index 000000000..429e19b4b --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_%.bbappend @@ -0,0 +1,15 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +# Only start the fabric manager when NVSwitch hardware is present, so the +# service is silently skipped (not failed) on non-NVSwitch instances. +SRC_URI += "file://nvidia-fabricmanager-nvswitch-condition.conf" + +RDEPENDS:${PN} += "nvidia-gpu-detect kmod" + +do_install:append() { + install -d ${D}${systemd_system_unitdir}/nvidia-fabricmanager.service.d + install -m 0644 ${UNPACKDIR}/nvidia-fabricmanager-nvswitch-condition.conf \ + ${D}${systemd_system_unitdir}/nvidia-fabricmanager.service.d/10-nvswitch-condition.conf +} + +FILES:${PN} += "${systemd_system_unitdir}/nvidia-fabricmanager.service.d/10-nvswitch-condition.conf" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_580.105.08.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_580.105.08.bb new file mode 100644 index 000000000..e5b3f8f25 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_580.105.08.bb @@ -0,0 +1,64 @@ +SUMMARY = "NVIDIA Fabric Manager for NVSwitch systems" +DESCRIPTION = "NVIDIA Fabric Manager provides NVSwitch management for NVIDIA HGX and DGX systems" +HOMEPAGE = "https://developer.nvidia.com/" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2cc00be68c1227a7c42ff3620ef75d05" + +SRC_URI = "https://developer.download.nvidia.com/compute/nvidia-driver/redist/fabricmanager/linux-x86_64/fabricmanager-linux-x86_64-${PV}-archive.tar.xz" +SRC_URI[sha256sum] = "eb3a81d004de426dee9b0f1332093828edb197125ef57a2e6500a95df3332d0b" + +S = "${UNPACKDIR}/fabricmanager-linux-x86_64-${PV}-archive" + +DEPENDS = "" +RDEPENDS:${PN} = "bash zlib" + +INSANE_SKIP:${PN} = "already-stripped ldflags" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +inherit systemd + +SYSTEMD_AUTO_ENABLE = "enable" +SYSTEMD_SERVICE:${PN} = "nvidia-fabricmanager.service" + +do_install() { + # Create directories + install -d ${D}${bindir} + install -d ${D}${libdir} + install -d ${D}${datadir}/nvidia/nvswitch + install -d ${D}${systemd_system_unitdir} + + # Install binaries + install -m 0755 ${S}/bin/nv-fabricmanager ${D}${bindir} + install -m 0755 ${S}/bin/nvidia-fabricmanager-start.sh ${D}${bindir} + install -m 0755 ${S}/bin/nvswitch-audit ${D}${bindir} + + # Install libraries + install -m 0644 ${S}/lib/libnvfm.so.1 ${D}${libdir} + ln -sf libnvfm.so.1 ${D}${libdir}/libnvfm.so + + # Install config files + install -m 0644 ${S}/etc/fabricmanager.cfg ${D}${datadir}/nvidia/nvswitch/ + install -m 0644 ${S}/etc/fabricmanager_multinode.cfg ${D}${datadir}/nvidia/nvswitch/ + + # Install topology files + for f in ${S}/share/nvidia/nvswitch/*; do + if [ -f "$f" ]; then + install -m 0644 "$f" ${D}${datadir}/nvidia/nvswitch/ + fi + done + + # Install systemd service + install -m 0644 ${S}/systemd/nvidia-fabricmanager.service ${D}${systemd_system_unitdir} +} + +FILES:${PN} = "\ + ${bindir}/nv-fabricmanager \ + ${bindir}/nvidia-fabricmanager-start.sh \ + ${bindir}/nvswitch-audit \ + ${libdir}/libnvfm.so.1 \ + ${libdir}/libnvfm.so \ + ${datadir}/nvidia/nvswitch/* \ + ${systemd_system_unitdir}/nvidia-fabricmanager.service \ +" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_580.95.05.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_580.95.05.bb new file mode 100644 index 000000000..11c4a1ef7 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_580.95.05.bb @@ -0,0 +1,61 @@ +SUMMARY = "NVIDIA Fabric Manager for NVSwitch systems" +DESCRIPTION = "NVIDIA Fabric Manager provides NVSwitch management for NVIDIA HGX and DGX systems" +HOMEPAGE = "https://developer.nvidia.com/" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2cc00be68c1227a7c42ff3620ef75d05" + +SRC_URI = "https://developer.download.nvidia.com/compute/nvidia-driver/redist/fabricmanager/linux-x86_64/fabricmanager-linux-x86_64-${PV}-archive.tar.xz" +SRC_URI[md5sum] = "a6568aa288cb4784b85ba6826463f918" +SRC_URI[sha256sum] = "f0220bfb67d04b4107acf00cc95abe5a9268fd8f8b5bae26971f4df232e4369c" + +S = "${UNPACKDIR}/fabricmanager-linux-x86_64-${PV}-archive" + +DEPENDS = "" +RDEPENDS:${PN} = "bash zlib" + +INSANE_SKIP:${PN} = "already-stripped ldflags" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +inherit systemd + +SYSTEMD_AUTO_ENABLE = "enable" +SYSTEMD_SERVICE:${PN} = "nvidia-fabricmanager.service" + +do_install() { + # Create directories + install -d ${D}${bindir} + install -d ${D}${libdir} + install -d ${D}${datadir}/nvidia/nvswitch + install -d ${D}${systemd_system_unitdir} + + # Install binaries + install -m 0755 ${S}/bin/nv-fabricmanager ${D}${bindir} + install -m 0755 ${S}/bin/nvidia-fabricmanager-start.sh ${D}${bindir} + install -m 0755 ${S}/bin/nvswitch-audit ${D}${bindir} + + # Install libraries + install -m 0644 ${S}/lib/libnvfm.so.1 ${D}${libdir} + ln -sf libnvfm.so.1 ${D}${libdir}/libnvfm.so + + # Install config + topology files (glob picks up new SKUs in future archives) + install -m 0644 ${S}/etc/fabricmanager.cfg ${D}${datadir}/nvidia/nvswitch/ + install -m 0644 ${S}/etc/fabricmanager_multinode.cfg ${D}${datadir}/nvidia/nvswitch/ + for f in ${S}/share/nvidia/nvswitch/*; do + [ -f "$f" ] && install -m 0644 "$f" ${D}${datadir}/nvidia/nvswitch/ + done + + # Install systemd service + install -m 0644 ${S}/systemd/nvidia-fabricmanager.service ${D}${systemd_system_unitdir} +} + +FILES:${PN} = "\ + ${bindir}/nv-fabricmanager \ + ${bindir}/nvidia-fabricmanager-start.sh \ + ${bindir}/nvswitch-audit \ + ${libdir}/libnvfm.so.1 \ + ${libdir}/libnvfm.so \ + ${datadir}/nvidia/nvswitch/* \ + ${systemd_system_unitdir}/nvidia-fabricmanager.service \ +" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_595.58.03.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_595.58.03.bb new file mode 100644 index 000000000..c090ad3ac --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-fabricmanager_595.58.03.bb @@ -0,0 +1,61 @@ +SUMMARY = "NVIDIA Fabric Manager for NVSwitch systems" +DESCRIPTION = "NVIDIA Fabric Manager provides NVSwitch management for NVIDIA HGX and DGX systems" +HOMEPAGE = "https://developer.nvidia.com/" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://LICENSE;md5=2cc00be68c1227a7c42ff3620ef75d05" + +SRC_URI = "https://developer.download.nvidia.com/compute/nvidia-driver/redist/fabricmanager/linux-x86_64/fabricmanager-linux-x86_64-${PV}-archive.tar.xz" +SRC_URI[md5sum] = "8761b4b5f371b7e87b2b82141c901311" +SRC_URI[sha256sum] = "ea86951987ced2c2bbed1a9a9037e917dd2268add0ec484db9216e843d8b7de8" + +S = "${UNPACKDIR}/fabricmanager-linux-x86_64-${PV}-archive" + +DEPENDS = "" +RDEPENDS:${PN} = "bash zlib" + +INSANE_SKIP:${PN} = "already-stripped ldflags" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +inherit systemd + +SYSTEMD_AUTO_ENABLE = "enable" +SYSTEMD_SERVICE:${PN} = "nvidia-fabricmanager.service" + +do_install() { + # Create directories + install -d ${D}${bindir} + install -d ${D}${libdir} + install -d ${D}${datadir}/nvidia/nvswitch + install -d ${D}${systemd_system_unitdir} + + # Install binaries + install -m 0755 ${S}/bin/nv-fabricmanager ${D}${bindir} + install -m 0755 ${S}/bin/nvidia-fabricmanager-start.sh ${D}${bindir} + install -m 0755 ${S}/bin/nvswitch-audit ${D}${bindir} + + # Install libraries + install -m 0644 ${S}/lib/libnvfm.so.1 ${D}${libdir} + ln -sf libnvfm.so.1 ${D}${libdir}/libnvfm.so + + # Install config + topology files (glob picks up new SKUs in future archives) + install -m 0644 ${S}/etc/fabricmanager.cfg ${D}${datadir}/nvidia/nvswitch/ + install -m 0644 ${S}/etc/fabricmanager_multinode.cfg ${D}${datadir}/nvidia/nvswitch/ + for f in ${S}/share/nvidia/nvswitch/*; do + [ -f "$f" ] && install -m 0644 "$f" ${D}${datadir}/nvidia/nvswitch/ + done + + # Install systemd service + install -m 0644 ${S}/systemd/nvidia-fabricmanager.service ${D}${systemd_system_unitdir} +} + +FILES:${PN} = "\ + ${bindir}/nv-fabricmanager \ + ${bindir}/nvidia-fabricmanager-start.sh \ + ${bindir}/nvswitch-audit \ + ${libdir}/libnvfm.so.1 \ + ${libdir}/libnvfm.so \ + ${datadir}/nvidia/nvswitch/* \ + ${systemd_system_unitdir}/nvidia-fabricmanager.service \ +" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-gpu-detect_1.0.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-gpu-detect_1.0.bb new file mode 100644 index 000000000..036aa81b7 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-gpu-detect_1.0.bb @@ -0,0 +1,16 @@ +SUMMARY = "Detect NVIDIA GPU / NVSwitch presence for conditional systemd services" +DESCRIPTION = "Small sysfs-based helper used as a systemd ExecCondition= so that \ +GPU-only services (nvidia-persistenced, nvidia-fabricmanager) skip cleanly on \ +instances without a GPU or without NVSwitch, allowing a single merged image." +LICENSE = "CLOSED" + +SRC_URI = "file://nvidia-gpu-detect" + +S = "${UNPACKDIR}" + +do_install() { + install -d ${D}${bindir} + install -m 0755 ${UNPACKDIR}/nvidia-gpu-detect ${D}${bindir}/nvidia-gpu-detect +} + +FILES:${PN} = "${bindir}/nvidia-gpu-detect" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-kernel-module.inc b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-kernel-module.inc new file mode 100644 index 000000000..b26d64681 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-kernel-module.inc @@ -0,0 +1,16 @@ +inherit module + +S = "${NVIDIA_SRC}/kernel-open" + +do_compile[depends] += "virtual/kernel:do_shared_workdir" +EXTRA_OEMAKE:append = " SYSSRC=${STAGING_KERNEL_DIR} KERNEL_OUTPUT=${STAGING_KERNEL_BUILDDIR}" + +export ARCH = "${@bb.utils.contains('TARGET_ARCH', 'x86_64', 'x86_64', '', d)}" + +RPROVIDES:${PN} += " \ + kernel-module-nvidia-${KERNEL_VERSION} \ + kernel-module-nvidia-drm-${KERNEL_VERSION} \ + kernel-module-nvidia-modeset-${KERNEL_VERSION} \ + kernel-module-nvidia-peermem-${KERNEL_VERSION} \ + kernel-module-nvidia-uvm-${KERNEL_VERSION} \ +" \ No newline at end of file diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-libs.inc b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-libs.inc new file mode 100644 index 000000000..622eacd2a --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-libs.inc @@ -0,0 +1,76 @@ +DESCRIPTION = "Linker and Installer of Nvidia Kernel modules, libs and binaries" +SECTION = "Installer Tools" +MAINTAINER = "Nauman Shakir " + +RDEPENDS:${PN} += "${PN}-firmware ${PN}-private-libs" + +addlib() { + cp ${NVIDIA_SRC}/${1}.so.${PV} ${D}${libdir}/ + ln -sf ${1}.so.${PV} ${D}${libdir}/${1}.so.1 + ln -sf ${1}.so.1 ${D}${libdir}/${1}.so +} + +addbin() { + install -m 755 -D ${NVIDIA_SRC}/${1} ${D}${bindir}/${1} +} + +do_install:append() { + install -d ${D}${libdir} + install -d ${D}${bindir} + + addlib libnvidia-ml + addlib libnvidia-allocator + addlib libnvidia-eglcore + addlib libnvidia-encode + addlib libnvidia-glcore + addlib libnvidia-gpucomp + addlib libnvidia-ngx + addlib libnvidia-nvvm + addlib libnvidia-opencl + addlib libnvidia-rtcore + addlib libnvidia-tls + addlib libnvidia-cfg + addlib libnvidia-opticalflow + addlib libnvidia-glsi + addlib libnvidia-glvkspirv + addlib libcuda + addlib libnvcuvid + addlib libnvidia-pkcs11-openssl3 + addlib libnvidia-pkcs11 + addlib libnvidia-ptxjitcompiler + + cp ${NVIDIA_SRC}/libnvidia-api.so.1 ${D}${libdir}/ + ln -sf libnvidia-api.so.1 ${D}${libdir}/libnvidia-api.so + ln -sf libnvidia-nvvm.so.${PV} ${D}${libdir}/libnvidia-nvvm.so.4 + + addbin nvidia-smi + addbin nvidia-debugdump + addbin nvidia-persistenced + addbin nvidia-modprobe + + # Copy the firmware to the target directory + install -d ${D}${libdir}/firmware/nvidia/${PV} + cp ${NVIDIA_SRC}/firmware/* ${D}${libdir}/firmware/nvidia/${PV}/ +} + +FILES:${PN}:append = " ${libdir} ${bindir}" + + +INHIBIT_PACKAGE_DEBUG_SPLIT = "1" +INHIBIT_PACKAGE_STRIP = "1" + +INSANE_SKIP:${PN}:append = " ldflags already-stripped dev-so" +INSANE_SKIP_libgl-nvidia:append = " ldflags" +INSANE_SKIP_:append = " ldflags" + +# Create a separate package for GSP firmware files +PACKAGES =+ "${PN}-firmware" +FILES:${PN}-firmware = "${libdir}/firmware/nvidia/${PV}/*" +FILES:${PN}:remove = "${libdir}/firmware/nvidia/${PV}/*" +INSANE_SKIP:${PN}-firmware += "arch" + +PACKAGES =+ "${PN}-private-libs" +FILES:${PN}-private-libs = "${libdir}/libnvidia-pkcs11.so*" +INSANE_SKIP:${PN}-private-libs:append = " ldflags already-stripped dev-so" +EXCLUDE_PACKAGES_FROM_SHLIBS = "${PN}-private-libs" +SKIP_FILEDEPS:${PN}-private-libs = "1" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-modprobe-config_1.0.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-modprobe-config_1.0.bb new file mode 100644 index 000000000..b2c062e14 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-modprobe-config_1.0.bb @@ -0,0 +1,18 @@ +SUMMARY = "NVIDIA kernel module configuration" +DESCRIPTION = "Configuration for NVIDIA kernel modules with dynamic settings based on kernel command line" +LICENSE = "CLOSED" + +SRC_URI = "\ + file://nvidia.conf \ +" + +S = "${UNPACKDIR}" + +do_install() { + install -d ${D}${sysconfdir}/modprobe.d + install -m 0644 ${UNPACKDIR}/nvidia.conf ${D}${sysconfdir}/modprobe.d/ +} + +FILES:${PN} = "${sysconfdir}/modprobe.d/nvidia.conf" + +RDEPENDS:${PN} = "bash" diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-persistenced_1.0.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-persistenced_1.0.bb new file mode 100644 index 000000000..8a9cf8b33 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia-persistenced_1.0.bb @@ -0,0 +1,21 @@ +SUMMARY = "NVidia Persistenced systemd service" +LICENSE = "CLOSED" + +SRC_URI += "\ + file://nvidia-persistenced.service \ +" + +S = "${UNPACKDIR}" + +inherit systemd + +RDEPENDS:${PN} += "nvidia-gpu-detect kmod" + +SYSTEMD_PACKAGES = "${PN}" +SYSTEMD_SERVICE:${PN} = "nvidia-persistenced.service" +SYSTEMD_AUTO_ENABLE:${PN} = "enable" + +do_install() { + install -d ${D}${systemd_unitdir}/system + install -m 0644 ${UNPACKDIR}/nvidia-persistenced.service ${D}${systemd_unitdir}/system +} diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_580.105.08.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_580.105.08.bb new file mode 100644 index 000000000..33f56b5bb --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_580.105.08.bb @@ -0,0 +1,23 @@ +SUMMARY = "NVidia Graphics Driver" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://../LICENSE;md5=92aa2e2af6aa0bcba1c3fe49da021937" + +NVIDIA_ARCHIVE_NAME = "NVIDIA-Linux-${TARGET_ARCH}-${PV}" +NVIDIA_SRC = "${UNPACKDIR}/${NVIDIA_ARCHIVE_NAME}" +SRC_URI = " \ + https://us.download.nvidia.com/tesla/${PV}/${NVIDIA_ARCHIVE_NAME}.run \ +" +SRC_URI[sha256sum] = "d9c6e8188672f3eb74dd04cfa69dd58479fa1d0162c8c28c8d17625763293475" + +RDEPENDS:${PN} = "nvidia-modprobe-config" + +do_unpack() { + chmod +x ${DL_DIR}/${NVIDIA_ARCHIVE_NAME}.run + rm -rf ${NVIDIA_SRC} + ${DL_DIR}/${NVIDIA_ARCHIVE_NAME}.run -x --target ${NVIDIA_SRC} +} + +do_make_scripts[noexec] = "1" + +include nvidia-kernel-module.inc +include nvidia-libs.inc diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_580.95.05.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_580.95.05.bb new file mode 100644 index 000000000..3f0677a12 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_580.95.05.bb @@ -0,0 +1,24 @@ +SUMMARY = "NVidia Graphics Driver" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://../LICENSE;md5=92aa2e2af6aa0bcba1c3fe49da021937" + +NVIDIA_ARCHIVE_NAME = "NVIDIA-Linux-${TARGET_ARCH}-${PV}" +NVIDIA_SRC = "${WORKDIR}/${NVIDIA_ARCHIVE_NAME}" +SRC_URI = " \ + https://us.download.nvidia.com/tesla/${PV}/${NVIDIA_ARCHIVE_NAME}.run \ +" +SRC_URI[md5sum] = "3d23653c4898d08b1f3f031ea8cdaa93" +SRC_URI[sha256sum] = "849ef0ef8e842b9806b2cde9f11c1303d54f1a9a769467e4e5d961b2fe1182a7" + +RDEPENDS:${PN} = "nvidia-modprobe-config" + +do_unpack() { + chmod +x ${DL_DIR}/${NVIDIA_ARCHIVE_NAME}.run + rm -rf ${NVIDIA_SRC} + ${DL_DIR}/${NVIDIA_ARCHIVE_NAME}.run -x --target ${NVIDIA_SRC} +} + +do_make_scripts[noexec] = "1" + +include nvidia-kernel-module.inc +include nvidia-libs.inc diff --git a/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_595.58.03.bb b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_595.58.03.bb new file mode 100644 index 000000000..8c0ab3d5c --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-graphics/nvidia/nvidia_595.58.03.bb @@ -0,0 +1,24 @@ +SUMMARY = "NVidia Graphics Driver" +LICENSE = "NVIDIA-Proprietary" +LIC_FILES_CHKSUM = "file://../LICENSE;md5=92aa2e2af6aa0bcba1c3fe49da021937" + +NVIDIA_ARCHIVE_NAME = "NVIDIA-Linux-${TARGET_ARCH}-${PV}" +NVIDIA_SRC = "${WORKDIR}/${NVIDIA_ARCHIVE_NAME}" +SRC_URI = " \ + https://us.download.nvidia.com/tesla/${PV}/${NVIDIA_ARCHIVE_NAME}.run \ +" +SRC_URI[md5sum] = "8d98a183bf994af0ff19980e0ef430f2" +SRC_URI[sha256sum] = "8c0d4f967b7932c4ab5714272aee8103392b0a702c92afa555176d36205829f9" + +RDEPENDS:${PN} = "nvidia-modprobe-config" + +do_unpack() { + chmod +x ${DL_DIR}/${NVIDIA_ARCHIVE_NAME}.run + rm -rf ${NVIDIA_SRC} + ${DL_DIR}/${NVIDIA_ARCHIVE_NAME}.run -x --target ${NVIDIA_SRC} +} + +do_make_scripts[noexec] = "1" + +include nvidia-kernel-module.inc +include nvidia-libs.inc diff --git a/os/yocto/layers/meta-nvidia/recipes-kernel/linux/files/nvidia.cfg b/os/yocto/layers/meta-nvidia/recipes-kernel/linux/files/nvidia.cfg new file mode 100644 index 000000000..9e4b7776d --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-kernel/linux/files/nvidia.cfg @@ -0,0 +1,3 @@ +CONFIG_CRYPTO_ECC=y +CONFIG_CRYPTO_ECDSA=y +CONFIG_CRYPTO_ECDH=y diff --git a/os/yocto/layers/meta-nvidia/recipes-kernel/linux/files/nvidia.scc b/os/yocto/layers/meta-nvidia/recipes-kernel/linux/files/nvidia.scc new file mode 100644 index 000000000..8bb149af1 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-kernel/linux/files/nvidia.scc @@ -0,0 +1,3 @@ +define KFEATURE_DESCRIPTION "NVIDIA configuration" + +kconf non-hardware nvidia.cfg diff --git a/os/yocto/layers/meta-nvidia/recipes-kernel/linux/linux-yocto%.bbappend b/os/yocto/layers/meta-nvidia/recipes-kernel/linux/linux-yocto%.bbappend new file mode 100644 index 000000000..08a530bb1 --- /dev/null +++ b/os/yocto/layers/meta-nvidia/recipes-kernel/linux/linux-yocto%.bbappend @@ -0,0 +1,7 @@ + +FILESEXTRAPATHS:prepend := "${THISDIR}/files:" + +SRC_URI += "file://nvidia.cfg \ + file://nvidia.scc" + +KERNEL_FEATURES:append = " nvidia.scc" diff --git a/os/yocto/mk.d/.gitignore b/os/yocto/mk.d/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/os/yocto/mkimage.sh b/os/yocto/mkimage.sh new file mode 100755 index 000000000..5260b979d --- /dev/null +++ b/os/yocto/mkimage.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Compatibility entrypoint: export Yocto-specific outputs, then invoke the +# backend-neutral image assembler. +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +BB_BUILD_DIR=${BB_BUILD_DIR:-build} +DIST_NAME= +FLAVOR= + +while [ $# -gt 0 ]; do + case "$1" in + --dist-name) + DIST_NAME=$2 + shift 2 + ;; + --flavor) + FLAVOR=$2 + shift 2 + ;; + -h|--help) + exec "$SCRIPT_DIR/scripts/export-artifacts.sh" --help + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +if [ -z "$DIST_NAME" ] || [ -z "$FLAVOR" ]; then + echo "Error: --dist-name and --flavor are required" >&2 + exit 1 +fi + +ARTIFACT_DIR=${ARTIFACT_DIR:-"$BB_BUILD_DIR/dstack-artifacts/$FLAVOR"} +"$SCRIPT_DIR/scripts/export-artifacts.sh" \ + --dist-name "$DIST_NAME" \ + --flavor "$FLAVOR" \ + --build-dir "$BB_BUILD_DIR" \ + --output-dir "$ARTIFACT_DIR" + +exec "$SCRIPT_DIR/../image/assemble.sh" \ + --manifest "$ARTIFACT_DIR/artifact-manifest.json" diff --git a/os/yocto/repro-build/.gitignore b/os/yocto/repro-build/.gitignore new file mode 100644 index 000000000..4c119cb1f --- /dev/null +++ b/os/yocto/repro-build/.gitignore @@ -0,0 +1,4 @@ +/build-a +/build-b +/dist +*.tar.gz diff --git a/os/yocto/repro-build/Dockerfile.repro b/os/yocto/repro-build/Dockerfile.repro new file mode 100644 index 000000000..0d1fdc540 --- /dev/null +++ b/os/yocto/repro-build/Dockerfile.repro @@ -0,0 +1,53 @@ +FROM ubuntu:24.04@sha256:3afff29dffbc200d202546dc6c4f614edc3b109691e7ab4aa23d02b42ba86790 + +# Prevent timezone prompt by setting noninteractive frontend and configuring tzdata +ENV DEBIAN_FRONTEND=noninteractive \ + TZ=Etc/UTC \ + TZDATA=Etc/UTC \ + LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 + +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH + +# Set timezone +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# Install required packages +RUN apt update && apt install -y \ + build-essential \ + chrpath \ + diffstat \ + lz4 \ + wireguard-tools \ + python3 \ + locales \ + ca-certificates \ + curl \ + git \ + file \ + gawk \ + wget \ + libclang-dev \ + xorriso \ + cpio \ + zstd \ + gdisk \ + dosfstools \ + mtools \ + libsqlite3-dev + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ + sh -s -- -y --profile minimal --default-toolchain 1.92 && \ + rustup component add rustfmt clippy rust-analyzer && \ + rustup target add wasm32-unknown-unknown x86_64-unknown-linux-musl thumbv6m-none-eabi && \ + chmod -R a+rX "$RUSTUP_HOME" "$CARGO_HOME" && \ + cargo --version && \ + rustc --version + +# The image runs as the caller's uid, so keep Cargo's registry/cache writable. +ENV CARGO_HOME=/tmp/cargo + +# Generate locale for bitbake +RUN locale-gen en_US.UTF-8 diff --git a/os/yocto/repro-build/check.sh b/os/yocto/repro-build/check.sh new file mode 100755 index 000000000..68cde6570 --- /dev/null +++ b/os/yocto/repro-build/check.sh @@ -0,0 +1,200 @@ +#!/bin/bash + +THIS_DIR=$(cd $(dirname $0); pwd) + +# Create a unique temporary directory and clean it up on exit +TEMP_DIR=$(mktemp -d) +trap 'rm -rf "$TEMP_DIR"' EXIT + +ACTION=$1 + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' + +IMAGE_NAME=${IMAGE_NAME:-dstack-rootfs} +ROOTFS_PATH=tmp/work/${DSTACK_MACHINE:-dstack}-poky-linux/${IMAGE_NAME}/1.0/rootfs +BUILD_DIR_A=${1:-${THIS_DIR}/build-a} +BUILD_DIR_B=${2:-${THIS_DIR}/build-b} +BB_DIR_A=${BB_DIR_A:-${BUILD_DIR_A}/bb-build} +BB_DIR_B=${BB_DIR_B:-${BUILD_DIR_B}/bb-build} +ROOTFS_A=${BB_DIR_A}/${ROOTFS_PATH} +ROOTFS_B=${BB_DIR_B}/${ROOTFS_PATH} + +# Only compare these image paths (relative to $BUILD_DIR_*/images). +# Everything else is ignored to avoid known non-reproducible artifacts. +COMPARE_IMAGE_WHITELIST=( + "bzImage" + "digest.txt" + "initramfs.cpio.gz" + "metadata.json" + "measurement.gcp.cbor" + "measurement.snp.cbor" + "measurement.tdx.cbor" + "ovmf.fd" + "rootfs.img.parted.verity" + "sha256sum.txt" + "auth_hash.txt" + "gcp/efi-root/EFI/BOOT/BOOTX64.EFI" +) + +is_whitelisted_image() { + local rel_path="$1" + local item + for item in "${COMPARE_IMAGE_WHITELIST[@]}"; do + if [ "$rel_path" = "$item" ] || [[ "$rel_path" == */"$item" ]]; then + return 0 + fi + done + return 1 +} + +check_files() { + local path_a="$1" + local path_b="$2" + local rel_path="$3" + + if [ ! -e "$path_a" ]; then + if [ -e "$path_b" ]; then + echo -e "${RED}File missing in A: $path_b" + return 1 + fi + return 0 + fi + if [ ! -e "$path_b" ]; then + if [ -e "$path_a" ]; then + echo -e "${RED}File missing in B: $path_a" + return 1 + fi + return 0 + fi + + if [ -d "$path_a" ]; then + if [ ! -d "$path_b" ]; then + echo -e "${RED}Path type mismatch: $rel_path is directory in A but not in B${NC}" + return 1 + fi + + local differences=0 + while IFS= read -r -d '' file; do + local rel_file="${file#$path_a/}" + check_files "$path_a/$rel_file" "$path_b/$rel_file" "$rel_path/$rel_file" + differences=$((differences + $?)) + done < <(find "$path_a" -maxdepth 1 -mindepth 1 -print0) + + return $differences + else + if [ ! -f "$path_b" ] && [ ! -L "$path_b" ]; then + echo -e "${RED}Path type mismatch: $rel_path is file in A but not in B${NC}" + ls -l $path_a + ls -l $path_b + return 1 + fi + + # Skip symlinks + if [ -L "$path_a" ] || [ -L "$path_b" ]; then + local link_a=$(readlink "$path_a") + local link_b=$(readlink "$path_b") + if [ "$link_a" != "$link_b" ]; then + echo -e "${RED}Symlink mismatch for $rel_path:${NC}" + echo -e "${RED}A: $link_a${NC}" + echo -e "${RED}B: $link_b${NC}" + return 1 + fi + return 0 + fi + + # Compare regular files + local hash_a=$(md5sum "$path_a" | cut -d' ' -f1) + local hash_b=$(md5sum "$path_b" | cut -d' ' -f1) + + if [ "$hash_a" != "$hash_b" ]; then + echo -e "${RED}Hash mismatch for $rel_path:${NC}" + echo -e "${RED}A: $hash_a${NC}" + echo -e "${RED}B: $hash_b${NC}" + analyze "$path_a" "$path_b" + return 1 + else + echo -e "${GREEN}Match for $rel_path${NC}" + return 0 + fi + fi +} + +analyze() { + local BIN_A=$1 + local BIN_B=$2 + + echo -e "\n${GREEN}Analyzing $BIN_A...${NC}" + ls -l $BIN_A + file $BIN_A + readelf -n $BIN_A + readelf -p .comment $BIN_A 2>/dev/null || true + + echo -e "\n${GREEN}Analyzing $BIN_B...${NC}" + ls -l $BIN_B + file $BIN_B + readelf -n $BIN_B + readelf -p .comment $BIN_B 2>/dev/null || true + + echo -e "\n${GREEN}Binary diff analysis:${NC}" + # Create hex dumps and compare + objdump -s $BIN_A > "$TEMP_DIR/bin_a.hex" + objdump -s $BIN_B > "$TEMP_DIR/bin_b.hex" + + echo "Differences:" + diff -u "$TEMP_DIR/bin_a.hex" "$TEMP_DIR/bin_b.hex" | grep -A 5 '^[^+-]' | head -n 100 +} + +check_images() { + echo -e "${YELLOW}Checking image files...${NC}" + local differences=0 + while IFS= read -r file_a; do + rel_path=$(echo "$file_a" | sed "s#${BUILD_DIR_A}/images/##g") + file_b="$BUILD_DIR_B/images/$rel_path" + if ! is_whitelisted_image "$rel_path"; then + continue + fi + if [ ! -f "$file_b" ]; then + echo -e "${RED}$rel_path is not found in $BUILD_DIR_B/images/${NC}" + differences=$((differences + 1)) + continue + fi + hash_a=$(md5sum "$file_a" | cut -d' ' -f 1) + hash_b=$(md5sum "$file_b" | cut -d' ' -f 1) + if [ "$hash_a" != "$hash_b" ]; then + echo -e "${RED}Hash mismatch for $rel_path:${NC}" + echo -e "${RED}$hash_a $file_a${NC}" + echo -e "${RED}$hash_b $file_b${NC}" + differences=$((differences + 1)) + else + echo -e "${GREEN}Match for $rel_path${NC}" + fi + done < <(find "$BUILD_DIR_A/images" -type f) + return $differences +} + +check() { + echo -e "${YELLOW}Checking reproducibility...${NC}" + + if check_images; then + return 0 + fi + + echo -e "${YELLOW}Checking rootfs...${NC}: $ROOTFS_A -> $ROOTFS_B" + local differences=0 + check_files "$ROOTFS_A" "$ROOTFS_B" "" + differences=$? + + if [ $differences -eq 0 ]; then + echo -e "\n${GREEN}All files are identical!${NC}" + return 0 + else + echo -e "\n${RED}Found $differences differences${NC}" + return 1 + fi +} + +check diff --git a/os/yocto/repro-build/repro-build.sh b/os/yocto/repro-build/repro-build.sh new file mode 100755 index 000000000..978a35682 --- /dev/null +++ b/os/yocto/repro-build/repro-build.sh @@ -0,0 +1,92 @@ +#! /bin/bash +set -e + +usage() { + echo "Usage: $0 [-n]" + echo " -n: Don't check reproducibility" + echo "" + echo "Environment:" + echo " RELEASE_FLAVORS: space-separated flavors to build (default: prod)" + echo " e.g. RELEASE_FLAVORS=\"prod dev\" $0" +} + +NO_CHECK=0 +while getopts ":n" opt; do + case $opt in + n) + NO_CHECK=1 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + usage + exit 1 + ;; + esac +done + + +BUILDER_NAME=dstack-build +THIS_DIR=$(cd $(dirname $0); pwd) +REPO_ROOT=${REPO_ROOT:-$(realpath "$THIS_DIR/../../..")} +GIT_DIR=$REPO_ROOT + +HOST_BUILD_DIR_A=${THIS_DIR}/build-a +HOST_BUILD_DIR_B=${THIS_DIR}/build-b + +# guest dirs +GUEST_BUILD_DIR=/dstack-build +GUEST_SRC_DIR=/dstack-src + +cd $THIS_DIR + +mkdir -p .dummy +(cd .dummy && docker build --platform linux/amd64 -t $BUILDER_NAME -f ../Dockerfile.repro .) +rm -rf .dummy + +build_to() { + mkdir -p $1 + GIT_REVISION=$(git -C "$REPO_ROOT" rev-parse HEAD) + BUILD_CMD="DSTACK_GIT_REVISION='$GIT_REVISION' ${2} ${GUEST_SRC_DIR}/os/build.sh --backend yocto --build-dir ./bb-build" + docker run --platform linux/amd64 --rm \ + --userns=host \ + --user $(id -u):$(id -g) \ + -v $REPO_ROOT:$GUEST_SRC_DIR \ + -v $1:$GUEST_BUILD_DIR \ + -w $GUEST_BUILD_DIR \ + $BUILDER_NAME bash -e -c "$BUILD_CMD" +} + +# Build production by default; callers may override, e.g. RELEASE_FLAVORS="prod dev". +RELEASE_FLAVORS="${RELEASE_FLAVORS:-prod}" + +build_to $HOST_BUILD_DIR_A "FLAVORS='$RELEASE_FLAVORS' DSTACK_TAR_RELEASE=1" + +DIST_DIR=${THIS_DIR}/dist +mkdir -p $DIST_DIR +mv $HOST_BUILD_DIR_A/images/*.tar.gz $DIST_DIR/ +if [ $NO_CHECK -eq 0 ]; then + build_to $HOST_BUILD_DIR_B "FLAVORS='$RELEASE_FLAVORS'" + ${THIS_DIR}/check.sh $HOST_BUILD_DIR_A $HOST_BUILD_DIR_B +fi + +if [[ -n $(git -C $GIT_DIR status --porcelain) ]]; then + echo "The working tree is not clean, skip generating reproducible build command" + exit 0 +fi + +echo "Reproducible build commands:" +echo "===========================" +cat < /dev/null 2>&1; then + zgrep() { + zcat "$2" | grep "$1" + } +fi + +useColor=true +if [ "$NO_COLOR" = "1" ] || [ ! -t 1 ]; then + useColor=false +fi +kernelVersion="$(uname -r)" +kernelMajor="${kernelVersion%%.*}" +kernelMinor="${kernelVersion#$kernelMajor.}" +kernelMinor="${kernelMinor%%.*}" + +is_set() { + zgrep "CONFIG_$1=[y|m]" "$CONFIG" > /dev/null +} +is_set_in_kernel() { + zgrep "CONFIG_$1=y" "$CONFIG" > /dev/null +} +is_set_as_module() { + zgrep "CONFIG_$1=m" "$CONFIG" > /dev/null +} + +color() { + # if stdout is not a terminal, then don't do color codes. + if [ "$useColor" = "false" ]; then + return 0 + fi + codes= + if [ "$1" = 'bold' ]; then + codes='1' + shift + fi + if [ "$#" -gt 0 ]; then + code= + case "$1" in + # see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors + black) code=30 ;; + red) code=31 ;; + green) code=32 ;; + yellow) code=33 ;; + blue) code=34 ;; + magenta) code=35 ;; + cyan) code=36 ;; + white) code=37 ;; + esac + if [ "$code" ]; then + codes="${codes:+$codes;}$code" + fi + fi + printf '\033[%sm' "$codes" +} +wrap_color() { + text="$1" + shift + color "$@" + printf '%s' "$text" + color reset + echo +} + +wrap_good() { + echo "$(wrap_color "$1" white): $(wrap_color "$2" green)" +} +wrap_bad() { + echo "$(wrap_color "$1" bold): $(wrap_color "$2" bold red)" +} +wrap_warning() { + wrap_color >&2 "$*" red +} + +check_flag() { + if is_set_in_kernel "$1"; then + wrap_good "CONFIG_$1" 'enabled' + elif is_set_as_module "$1"; then + wrap_good "CONFIG_$1" 'enabled (as module)' + else + wrap_bad "CONFIG_$1" 'missing' + EXITCODE=1 + fi +} + +check_flags() { + for flag in "$@"; do + printf -- '- ' + check_flag "$flag" + done +} + +check_command() { + if command -v "$1" > /dev/null 2>&1; then + wrap_good "$1 command" 'available' + else + wrap_bad "$1 command" 'missing' + EXITCODE=1 + fi +} + +check_device() { + if [ -c "$1" ]; then + wrap_good "$1" 'present' + else + wrap_bad "$1" 'missing' + EXITCODE=1 + fi +} + +if [ ! -e "$CONFIG" ]; then + wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config ..." + for tryConfig in $possibleConfigs; do + if [ -e "$tryConfig" ]; then + CONFIG="$tryConfig" + break + fi + done + if [ ! -e "$CONFIG" ]; then + wrap_warning "error: cannot find kernel config" + wrap_warning " try running this script again, specifying the kernel config:" + wrap_warning " CONFIG=/path/to/kernel/.config $0 or $0 /path/to/kernel/.config" + exit 1 + fi +fi + +wrap_color "info: reading kernel config from $CONFIG ..." white +echo + +echo 'Generally Necessary:' + +printf -- '- ' +if [ "$(stat -f -c %t /sys/fs/cgroup 2> /dev/null)" = '63677270' ]; then + wrap_good 'cgroup hierarchy' 'cgroupv2' + cgroupv2ControllerFile='/sys/fs/cgroup/cgroup.controllers' + if [ -f "$cgroupv2ControllerFile" ]; then + echo ' Controllers:' + for controller in cpu cpuset io memory pids; do + if grep -qE '(^| )'"$controller"'($| )' "$cgroupv2ControllerFile"; then + echo " - $(wrap_good "$controller" 'available')" + else + echo " - $(wrap_bad "$controller" 'missing')" + fi + done + else + wrap_bad "$cgroupv2ControllerFile" 'nonexistent??' + fi + # TODO find an efficient way to check if cgroup.freeze exists in subdir +else + cgroupSubsystemDir="$(sed -rne '/^[^ ]+ ([^ ]+) cgroup ([^ ]*,)?(cpu|cpuacct|cpuset|devices|freezer|memory)[, ].*$/ { s//\1/p; q }' /proc/mounts)" + cgroupDir="$(dirname "$cgroupSubsystemDir")" + if [ -d "$cgroupDir/cpu" ] || [ -d "$cgroupDir/cpuacct" ] || [ -d "$cgroupDir/cpuset" ] || [ -d "$cgroupDir/devices" ] || [ -d "$cgroupDir/freezer" ] || [ -d "$cgroupDir/memory" ]; then + echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]" + else + if [ "$cgroupSubsystemDir" ]; then + echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]" + else + wrap_bad 'cgroup hierarchy' 'nonexistent??' + fi + EXITCODE=1 + echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)" + fi +fi + +if [ "$(cat /sys/module/apparmor/parameters/enabled 2> /dev/null)" = 'Y' ]; then + printf -- '- ' + if command -v apparmor_parser > /dev/null 2>&1; then + wrap_good 'apparmor' 'enabled and tools installed' + else + wrap_bad 'apparmor' 'enabled, but apparmor_parser missing' + printf ' ' + if command -v apt-get > /dev/null 2>&1; then + wrap_color '(use "apt-get install apparmor" to fix this)' + elif command -v yum > /dev/null 2>&1; then + wrap_color '(your best bet is "yum install apparmor-parser")' + else + wrap_color '(look for an "apparmor" package for your distribution)' + fi + EXITCODE=1 + fi +fi + +check_flags \ + NAMESPACES NET_NS PID_NS IPC_NS UTS_NS \ + CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG \ + KEYS \ + VETH BRIDGE BRIDGE_NETFILTER \ + IP_NF_FILTER IP_NF_MANGLE IP_NF_TARGET_MASQUERADE \ + NETFILTER_XT_MATCH_ADDRTYPE \ + NETFILTER_XT_MATCH_CONNTRACK \ + NETFILTER_XT_MATCH_IPVS \ + NETFILTER_XT_MARK \ + IP_NF_NAT NF_NAT \ + POSIX_MQUEUE +# (POSIX_MQUEUE is required for bind-mounting /dev/mqueue into containers) + +if [ "$kernelMajor" -lt 4 ] || ([ "$kernelMajor" -eq 4 ] && [ "$kernelMinor" -lt 8 ]); then + check_flags DEVPTS_MULTIPLE_INSTANCES +fi + +if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 1 ]; then + check_flags NF_NAT_IPV4 +fi + +if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 2 ]; then + check_flags NF_NAT_NEEDED +fi +# check availability of BPF_CGROUP_DEVICE support +if [ "$kernelMajor" -ge 5 ] || ([ "$kernelMajor" -eq 4 ] && [ "$kernelMinor" -ge 15 ]); then + check_flags CGROUP_BPF +fi + +echo + +echo 'Optional Features:' +{ + check_flags USER_NS +} +{ + check_flags SECCOMP + check_flags SECCOMP_FILTER +} +{ + check_flags CGROUP_PIDS +} +{ + check_flags MEMCG_SWAP + # Kernel v5.8+ removes MEMCG_SWAP_ENABLED. + if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 8 ]; then + CODE=${EXITCODE} + check_flags MEMCG_SWAP_ENABLED + # FIXME this check is cgroupv1-specific + if [ -e /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes ]; then + echo " $(wrap_color '(cgroup swap accounting is currently enabled)' bold black)" + EXITCODE=${CODE} + elif is_set MEMCG_SWAP && ! is_set MEMCG_SWAP_ENABLED; then + echo " $(wrap_color '(cgroup swap accounting is currently not enabled, you can enable it by setting boot option "swapaccount=1")' bold black)" + fi + else + # Kernel v5.8+ enables swap accounting by default. + echo " $(wrap_color '(cgroup swap accounting is currently enabled)' bold black)" + fi +} +{ + if is_set LEGACY_VSYSCALL_NATIVE; then + printf -- '- ' + wrap_bad "CONFIG_LEGACY_VSYSCALL_NATIVE" 'enabled' + echo " $(wrap_color '(dangerous, provides an ASLR-bypassing target with usable ROP gadgets.)' bold black)" + elif is_set LEGACY_VSYSCALL_EMULATE; then + printf -- '- ' + wrap_good "CONFIG_LEGACY_VSYSCALL_EMULATE" 'enabled' + elif is_set LEGACY_VSYSCALL_NONE; then + printf -- '- ' + wrap_bad "CONFIG_LEGACY_VSYSCALL_NONE" 'enabled' + echo " $(wrap_color '(containers using eglibc <= 2.13 will not work. Switch to' bold black)" + echo " $(wrap_color ' "CONFIG_VSYSCALL_[NATIVE|EMULATE]" or use "vsyscall=[native|emulate]"' bold black)" + echo " $(wrap_color ' on kernel command line. Note that this will disable ASLR for the,' bold black)" + echo " $(wrap_color ' VDSO which may assist in exploiting security vulnerabilities.)' bold black)" + # else Older kernels (prior to 3dc33bd30f3e, released in v4.40-rc1) do + # not have these LEGACY_VSYSCALL options and are effectively + # LEGACY_VSYSCALL_EMULATE. Even older kernels are presumably + # effectively LEGACY_VSYSCALL_NATIVE. + fi +} + +if [ "$kernelMajor" -lt 4 ] || ([ "$kernelMajor" -eq 4 ] && [ "$kernelMinor" -le 5 ]); then + check_flags MEMCG_KMEM +fi + +if [ "$kernelMajor" -lt 3 ] || ([ "$kernelMajor" -eq 3 ] && [ "$kernelMinor" -le 18 ]); then + check_flags RESOURCE_COUNTERS +fi + +if [ "$kernelMajor" -lt 3 ] || ([ "$kernelMajor" -eq 3 ] && [ "$kernelMinor" -le 13 ]); then + netprio=NETPRIO_CGROUP +else + netprio=CGROUP_NET_PRIO +fi + +if [ "$kernelMajor" -lt 5 ]; then + check_flags IOSCHED_CFQ CFQ_GROUP_IOSCHED +fi + +check_flags \ + BLK_CGROUP BLK_DEV_THROTTLING \ + CGROUP_PERF \ + CGROUP_HUGETLB \ + NET_CLS_CGROUP $netprio \ + CFS_BANDWIDTH FAIR_GROUP_SCHED \ + IP_NF_TARGET_REDIRECT \ + IP_VS \ + IP_VS_NFCT \ + IP_VS_PROTO_TCP \ + IP_VS_PROTO_UDP \ + IP_VS_RR \ + SECURITY_SELINUX \ + SECURITY_APPARMOR + +if ! is_set EXT4_USE_FOR_EXT2; then + check_flags EXT3_FS EXT3_FS_XATTR EXT3_FS_POSIX_ACL EXT3_FS_SECURITY + if ! is_set EXT3_FS || ! is_set EXT3_FS_XATTR || ! is_set EXT3_FS_POSIX_ACL || ! is_set EXT3_FS_SECURITY; then + echo " $(wrap_color '(enable these ext3 configs if you are using ext3 as backing filesystem)' bold black)" + fi +fi + +check_flags EXT4_FS EXT4_FS_POSIX_ACL EXT4_FS_SECURITY +if ! is_set EXT4_FS || ! is_set EXT4_FS_POSIX_ACL || ! is_set EXT4_FS_SECURITY; then + if is_set EXT4_USE_FOR_EXT2; then + echo " $(wrap_color 'enable these ext4 configs if you are using ext3 or ext4 as backing filesystem' bold black)" + else + echo " $(wrap_color 'enable these ext4 configs if you are using ext4 as backing filesystem' bold black)" + fi +fi + +echo '- Network Drivers:' +echo " - \"$(wrap_color 'overlay' blue)\":" +check_flags VXLAN BRIDGE_VLAN_FILTERING | sed 's/^/ /' +echo ' Optional (for encrypted networks):' +check_flags CRYPTO CRYPTO_AEAD CRYPTO_GCM CRYPTO_SEQIV CRYPTO_GHASH \ + XFRM XFRM_USER XFRM_ALGO INET_ESP NETFILTER_XT_MATCH_BPF | sed 's/^/ /' +if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 3 ]; then + check_flags INET_XFRM_MODE_TRANSPORT | sed 's/^/ /' +fi +echo " - \"$(wrap_color 'ipvlan' blue)\":" +check_flags IPVLAN | sed 's/^/ /' +echo " - \"$(wrap_color 'macvlan' blue)\":" +check_flags MACVLAN DUMMY | sed 's/^/ /' +echo " - \"$(wrap_color 'ftp,tftp client in container' blue)\":" +check_flags NF_NAT_FTP NF_CONNTRACK_FTP NF_NAT_TFTP NF_CONNTRACK_TFTP | sed 's/^/ /' + +# only fail if no storage drivers available +CODE=${EXITCODE} +EXITCODE=0 +STORAGE=1 + +echo '- Storage Drivers:' +echo " - \"$(wrap_color 'btrfs' blue)\":" +check_flags BTRFS_FS | sed 's/^/ /' +check_flags BTRFS_FS_POSIX_ACL | sed 's/^/ /' +[ "$EXITCODE" = 0 ] && STORAGE=0 +EXITCODE=0 + +echo " - \"$(wrap_color 'overlay' blue)\":" +check_flags OVERLAY_FS | sed 's/^/ /' +[ "$EXITCODE" = 0 ] && STORAGE=0 +EXITCODE=0 + +echo " - \"$(wrap_color 'zfs' blue)\":" +printf ' - ' +check_device /dev/zfs +printf ' - ' +check_command zfs +printf ' - ' +check_command zpool +[ "$EXITCODE" = 0 ] && STORAGE=0 +EXITCODE=0 + +EXITCODE=$CODE +[ "$STORAGE" = 1 ] && EXITCODE=1 + +echo + +check_limit_over() { + if [ "$(cat "$1")" -le "$2" ]; then + wrap_bad "- $1" "$(cat "$1")" + wrap_color " This should be set to at least $2, for example set: sysctl -w kernel/keys/root_maxkeys=1000000" bold black + EXITCODE=1 + else + wrap_good "- $1" "$(cat "$1")" + fi +} + +echo 'Limits:' +check_limit_over /proc/sys/kernel/keys/root_maxkeys 10000 +echo + +exit $EXITCODE diff --git a/os/yocto/scripts/export-artifacts.sh b/os/yocto/scripts/export-artifacts.sh new file mode 100755 index 000000000..7fcbd283a --- /dev/null +++ b/os/yocto/scripts/export-artifacts.sh @@ -0,0 +1,211 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +if [ -z "$DIST_NAME" ] || [ -z "$FLAVOR" ]; then + echo "Error: --dist-name and --flavor are required" >&2 + usage >&2 + exit 1 +fi + +case "$FLAVOR" in + prod) + if [[ "$DIST_NAME" == *-dev ]]; then + echo "Error: prod flavor requires a non-dev dist name: $DIST_NAME" >&2 + exit 1 + fi + IS_DEV=false + ;; + dev) + if [[ "$DIST_NAME" != *-dev ]]; then + echo "Error: dev flavor requires a dist name ending in -dev: $DIST_NAME" >&2 + exit 1 + fi + IS_DEV=true + ;; + *) + echo "Error: unsupported flavor '$FLAVOR' (expected prod or dev)" >&2 + exit 1 + ;; +esac + +if [ ! -d "$BUILD_DIR" ]; then + echo "Error: BitBake build directory not found: $BUILD_DIR" >&2 + exit 1 +fi +BUILD_DIR=$(realpath "$BUILD_DIR") +ARTIFACT_DIR=${ARTIFACT_DIR:-"$BUILD_DIR/dstack-artifacts/$FLAVOR"} +ARTIFACT_DIR=$(realpath -m "$ARTIFACT_DIR") +case "$ARTIFACT_DIR" in + /|"$BUILD_DIR"|"$REPO_ROOT"|"$YOCTO_DIR") + echo "Error: refusing unsafe artifact output directory: $ARTIFACT_DIR" >&2 + exit 1 + ;; +esac + +DSTACK_MACHINE=${DSTACK_MACHINE:-dstack} +COMMON_IMG_DIR="$BUILD_DIR/tmp/deploy/images/$DSTACK_MACHINE" +FLAVOR_IMG_DIR="$BUILD_DIR/tmp-mc-$FLAVOR/deploy/images/$DSTACK_MACHINE" +VERITY_ENV_FILE="$BUILD_DIR/tmp-mc-$FLAVOR/work-shared/$DSTACK_MACHINE/dm-verity/dstack-rootfs.squashfs.verity.env" + +if [ ! -f "$VERITY_ENV_FILE" ]; then + echo "Error: verity env not found: $VERITY_ENV_FILE" >&2 + echo "Build the rootfs first, e.g. bitbake mc:${FLAVOR}:dstack-rootfs" >&2 + exit 1 +fi +# shellcheck source=/dev/null +source "$VERITY_ENV_FILE" +: "${ROOT_HASH:?ROOT_HASH missing from verity environment}" +: "${DATA_SIZE:?DATA_SIZE missing from verity environment}" + +DSTACK_VERSION=$(bitbake-getvar --value DISTRO_VERSION | tail -1) +OVMF_VARIANT=$(bitbake-getvar --value OVMF_VARIANT -r dstack-ovmf) +if [ -z "$DSTACK_VERSION" ] || [ -z "$OVMF_VARIANT" ]; then + echo "Error: failed to read DISTRO_VERSION or OVMF_VARIANT from BitBake" >&2 + exit 1 +fi +GIT_REVISION=${DSTACK_GIT_REVISION:-} +if [ -z "$GIT_REVISION" ]; then + GIT_REVISION=$(git -C "$REPO_ROOT" rev-parse HEAD) +fi + +rm -rf "$ARTIFACT_DIR" +mkdir -p "$ARTIFACT_DIR/files" + +link_required() { + local source=$1 + local name=$2 + if [ ! -f "$source" ]; then + echo "Error: required Yocto artifact not found: $source" >&2 + exit 1 + fi + ln -s "$(realpath "$source")" "$ARTIFACT_DIR/files/$name" +} + +link_optional() { + local source=$1 + local name=$2 + if [ -f "$source" ]; then + ln -s "$(realpath "$source")" "$ARTIFACT_DIR/files/$name" + return 0 + fi + return 1 +} + +link_required "$COMMON_IMG_DIR/dstack-initramfs.cpio.gz" initramfs.cpio.gz +link_required "$COMMON_IMG_DIR/bzImage" bzImage +link_required "$COMMON_IMG_DIR/ovmf.fd" ovmf.fd +link_required "$FLAVOR_IMG_DIR/dstack-rootfs-${DSTACK_MACHINE}.squashfs.verity" rootfs.squashfs.verity + +FIRMWARE_SEV= +UKI= +if link_optional "$COMMON_IMG_DIR/ovmf-sev.fd" ovmf-sev.fd; then + FIRMWARE_SEV=files/ovmf-sev.fd +fi +if link_optional "$FLAVOR_IMG_DIR/dstack-uki.efi" dstack-uki.efi; then + UKI=files/dstack-uki.efi +fi + +python3 - \ + "$ARTIFACT_DIR/artifact-manifest.json" \ + "$DIST_NAME" "$DSTACK_VERSION" "$FLAVOR" "$IS_DEV" \ + "$GIT_REVISION" "$OVMF_VARIANT" "$ROOT_HASH" "$DATA_SIZE" \ + "$DSTACK_MACHINE" "$FIRMWARE_SEV" "$UKI" <<'PYMANIFEST' +import json +import sys + +( + output, + name, + version, + flavor, + is_dev, + git_revision, + ovmf_variant, + root_hash, + data_size, + machine, + firmware_sev, + uki, +) = sys.argv[1:] + +manifest = { + "schema_version": 1, + "backend": "yocto", + "image": { + "name": name, + "version": version, + "flavor": flavor, + "is_dev": is_dev == "true", + }, + "source": {"git_revision": git_revision}, + "boot": {"ovmf_variant": ovmf_variant}, + "verity": {"root_hash": root_hash, "data_size": data_size}, + "artifacts": { + "initramfs": "files/initramfs.cpio.gz", + "kernel": "files/bzImage", + "firmware": "files/ovmf.fd", + "rootfs_verity": "files/rootfs.squashfs.verity", + "firmware_sev": firmware_sev or None, + "uki": uki or None, + }, + "backend_metadata": {"machine": machine}, +} +with open(output, "w", encoding="utf-8") as file: + json.dump(manifest, file, indent=2) + file.write("\n") +PYMANIFEST + +echo "$ARTIFACT_DIR/artifact-manifest.json" diff --git a/os/yocto/setup.d/.gitignore b/os/yocto/setup.d/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/os/yocto/setup.d/nvidia-layer.sh b/os/yocto/setup.d/nvidia-layer.sh new file mode 100644 index 000000000..bd082a20a --- /dev/null +++ b/os/yocto/setup.d/nvidia-layer.sh @@ -0,0 +1,2 @@ +LAYERS="$LAYERS \ + $THIS_DIR/layers/meta-nvidia" diff --git a/os/yocto/tools/README.md b/os/yocto/tools/README.md new file mode 100644 index 000000000..caac234a4 --- /dev/null +++ b/os/yocto/tools/README.md @@ -0,0 +1,11 @@ +# Yocto host tools + +This directory is reserved for host-side workarounds that are specific to +building the Yocto backend. It is not part of the guest-OS backend interface; +the supported backend entrypoint is [`../build.sh`](../build.sh). + +- `fix-self-uid-map.sh` is an explicit host AppArmor workaround for affected + Ubuntu installations; it is not part of the image build. + +The legacy cross-cutting helpers live under [`../../../tools/`](../../../tools/) +instead of inside this backend. diff --git a/os/yocto/tools/fix-self-uid-map.sh b/os/yocto/tools/fix-self-uid-map.sh new file mode 100755 index 000000000..b262b9c47 --- /dev/null +++ b/os/yocto/tools/fix-self-uid-map.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# BUG: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/2056555 +sudo apparmor_parser -R /etc/apparmor.d/unprivileged_userns diff --git a/prek.toml b/prek.toml index 5015768ba..127d9c310 100644 --- a/prek.toml +++ b/prek.toml @@ -11,15 +11,15 @@ repo = "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/pre-commit/pre-commit-hooks" rev = "v5.0.0" hooks = [ - { id = "trailing-whitespace", args = ["--markdown-linebreak-ext=md"] }, - { id = "end-of-file-fixer" }, - { id = "check-yaml", args = ["--allow-multiple-documents"], exclude = "gateway/templates/" }, + { id = "trailing-whitespace", args = ["--markdown-linebreak-ext=md"], exclude = '^(os/yocto/|tools/(dev-stack\.sh|vm-runner/)|dstack/(dstack-util/tests/fixtures/key-provider-docker-compose\.yaml|gateway/src/main_service/snapshots/.*))' }, + { id = "end-of-file-fixer", exclude = "^(os/yocto/|tools/(dev-stack\\.sh|vm-runner/))" }, + { id = "check-yaml", args = ["--allow-multiple-documents"], exclude = "dstack/gateway/templates/" }, { id = "check-toml" }, { id = "check-json" }, { id = "check-merge-conflict" }, { id = "check-added-large-files", args = ["--maxkb=500"] }, { id = "check-symlinks" }, - { id = "mixed-line-ending", args = ["--fix=lf"] }, + { id = "mixed-line-ending", args = ["--fix=lf"], exclude = "^(os/yocto/|tools/(dev-stack\\.sh|vm-runner/))" }, ] # --- Rust: rustfmt --- @@ -29,20 +29,20 @@ repo = "local" [[repos.hooks]] id = "cargo-fmt" name = "cargo fmt" -entry = "cargo fmt --all" +entry = "cargo fmt --manifest-path dstack/Cargo.toml --all" language = "system" types = ["rust"] pass_filenames = false # --- Python: ruff (lint + format) --- -# scripts/bin/dstack-cloud is vendored from the dstack-cloud tree and follows its -# own style; exclude it so we don't reformat/relint a large third-party script. +# dstack-cloud and the imported Yocto/legacy tools follow their upstream styles; +# exclude them so this repository does not create unrelated formatting churn. [[repos]] repo = "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/astral-sh/ruff-pre-commit" rev = "v0.11.4" hooks = [ - { id = "ruff", args = ["--fix", "--select", "E,F,I,D", "--ignore", "D203,D213,E501"], exclude = "^scripts/bin/dstack-cloud$" }, - { id = "ruff-format", exclude = "^scripts/bin/dstack-cloud$" }, + { id = "ruff", args = ["--fix", "--select", "E,F,I,D", "--ignore", "D203,D213,E501"], exclude = "^(dstack/scripts/bin/dstack-cloud|os/yocto/|tools/vm-runner/)" }, + { id = "ruff-format", exclude = "^(dstack/scripts/bin/dstack-cloud|os/yocto/|tools/vm-runner/)" }, ] # --- Go: go vet --- @@ -62,7 +62,10 @@ pass_filenames = false repo = "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/shellcheck-py/shellcheck-py" rev = "v0.10.0.1" hooks = [ - { id = "shellcheck" }, + # The imported Yocto backend retains its upstream shell style. New common + # OS scripts remain covered by this hook. The explicit legacy list records + # pre-existing shellcheck debt exposed when the files moved into dstack/. + { id = "shellcheck", exclude = '^(os/yocto/.*|tools/(dev-stack\.sh|vm-runner/.*)|dstack/(build/shared/config-qemu\.sh|cargo-check-all\.sh|gateway/test-run/(cluster\.sh|e2e/run-e2e\.sh|test_certbot\.sh)|guest-agent-simulator/install-systemd\.sh|kms/auth-eth/run-tests\.sh|scripts/(config-fw\.sh|setup-bridge\.sh)|supervisor/tests/(test-cli\.sh|test\.sh)|test-scripts/(get-app-key\.sh|inspect-cert\.sh)|verifier/test\.sh|vmm/(src/setup-user\.sh|src/tests/test-deployment\.sh|ui/scripts/build_proto\.sh|venv\.sh)))$' }, ] # --- Conventional commits (used by cliff.toml for changelog) --- diff --git a/run.sh b/run.sh deleted file mode 100755 index bf8a2af98..000000000 --- a/run.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# SPDX-FileCopyrightText: © 2024 Phala Network -# -# SPDX-License-Identifier: Apache-2.0 - -VMDIR=$1 -IMAGE_PATH=./images/$(jq -r '.image' ${VMDIR}/vm-manifest.json) -IMG_METADATA=${IMAGE_PATH}/metadata.json -MEM=$(jq -r '.memory' ${VMDIR}/vm-manifest.json) -VCPUS=$(jq -r '.vcpu' ${VMDIR}/vm-manifest.json) - -VDA=${VMDIR}/hda.img - -PROCESS_NAME=qemu - -INITRD=${IMAGE_PATH}/$(jq -r '.initrd' ${IMG_METADATA}) -KERNEL=${IMAGE_PATH}/$(jq -r '.kernel' ${IMG_METADATA}) -CDROM=${IMAGE_PATH}/$(jq -r '.rootfs' ${IMG_METADATA}) -TDVF_FIRMWARE=${IMAGE_PATH}/$(jq -r '.bios' ${IMG_METADATA}) -CMDLINE=$(jq -r '.cmdline' ${IMG_METADATA}) -CONFIG_DIR=${VMDIR}/shared -TD=${TD:-1} -RO=${RO:-on} -CID=$(( ( RANDOM % 10000 ) + 3 )) - -ARGS="${ARGS} -kernel ${KERNEL}" -ARGS="${ARGS} -initrd ${INITRD}" - -echo INITRD=${INITRD} -echo ARGS=${ARGS} -echo VDA=${VDA} -echo CMDLINE=${CMDLINE} -echo TD=${TD} - -if [ "${TD}" == "1" ]; then - MACHINE_ARGS=",confidential-guest-support=tdx,hpet=off" - PROCESS_NAME=td - TDX_ARGS="-device vhost-vsock-pci,guest-cid=${CID} -object tdx-guest,id=tdx" -fi -BIOS="-bios ${TDVF_FIRMWARE}" - -sleep 2 - -qemu-system-x86_64 \ - -accel kvm \ - -m ${MEM}M -smp ${VCPUS} \ - -name ${PROCESS_NAME},process=${PROCESS_NAME},debug-threads=on \ - -cpu host \ - -machine q35,kernel_irqchip=split${MACHINE_ARGS} \ - ${BIOS} \ - ${TDX_ARGS} \ - -nographic \ - -nodefaults \ - -chardev stdio,id=ser0,signal=on -serial chardev:ser0 \ - -device virtio-net-pci,netdev=nic0_td -netdev user,id=nic0_td \ - -drive file=${VDA},if=none,id=virtio-disk0 -device virtio-blk-pci,drive=virtio-disk0 \ - -cdrom ${CDROM} \ - -virtfs local,path=${CONFIG_DIR},mount_tag=host-shared,readonly=${RO},security_model=mapped,id=virtfs0 \ - ${ARGS} \ - -append "${CMDLINE}" diff --git a/sdk/README.md b/sdk/README.md index 6d1b72a0b..17aa1b4fa 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -20,5 +20,5 @@ All SDKs communicate with the guest agent via HTTP over a Unix socket (`/var/run For local development without TDX hardware, use the simulator: - [Download releases](https://github.com/Dstack-TEE/dstack/releases?q=simulator-v&expanded=true) -- [Install as a systemd service](../guest-agent-simulator/install-systemd.sh) +- [Install as a systemd service](../dstack/guest-agent-simulator/install-systemd.sh) - [Docker image](https://hub.docker.com/r/phalanetwork/dstack-simulator) diff --git a/sdk/curl/api.md b/sdk/curl/api.md index bb2912d6c..4893f55bb 100644 --- a/sdk/curl/api.md +++ b/sdk/curl/api.md @@ -144,7 +144,7 @@ curl --unix-socket /var/run/dstack.sock http://dstack/GetQuote?report_data=00000 ``` **Note on Event Log:** -The `event_log` field contains a JSON array of TDX event log entries. For RTMR 0-2 (boot-time measurements), only the digest is included; the payload is stripped to reduce response size. For RTMR3 (runtime measurements), both digest and payload are included. To verify the event log, submit it along with the quote to the [verifier service](../../verifier/README.md). +The `event_log` field contains a JSON array of TDX event log entries. For RTMR 0-2 (boot-time measurements), only the digest is included; the payload is stripped to reduce response size. For RTMR3 (runtime measurements), both digest and payload are included. To verify the event log, submit it along with the quote to the [verifier service](../../dstack/verifier/README.md). ### 4. Get Info diff --git a/sdk/js/src/__tests__/get-compose-hash.test.ts b/sdk/js/src/__tests__/get-compose-hash.test.ts index 76673ce4a..79e64170b 100644 --- a/sdk/js/src/__tests__/get-compose-hash.test.ts +++ b/sdk/js/src/__tests__/get-compose-hash.test.ts @@ -13,13 +13,13 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", bash_script: "start.sh" } - + const compose2: AppCompose = { bash_script: "start.sh", docker_compose_file: "docker-compose.yml", runner: "docker-compose" } - + // Both should produce the same hash despite different key order expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) @@ -33,7 +33,7 @@ describe('Deterministic JSON Serialization', () => { beta: 42 } } as AppCompose - + const compose2: AppCompose = { nested_config: { alpha: "first", @@ -42,7 +42,7 @@ describe('Deterministic JSON Serialization', () => { }, runner: "docker-compose" } as AppCompose - + expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) @@ -66,7 +66,7 @@ describe('Deterministic JSON Serialization', () => { } } } as AppCompose - + const compose2: AppCompose = { config: { cache: { @@ -86,7 +86,7 @@ describe('Deterministic JSON Serialization', () => { }, runner: "docker-compose" } as AppCompose - + expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) }) @@ -97,12 +97,12 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", items: [3, 1, 2] } as AppCompose - + const compose2: AppCompose = { runner: "docker-compose", items: [1, 2, 3] } as AppCompose - + // Different array orders should produce different hashes expect(getComposeHash(compose1)).not.toBe(getComposeHash(compose2)) }) @@ -115,7 +115,7 @@ describe('Deterministic JSON Serialization', () => { { name: "db", port: 5432 } ] } as AppCompose - + const compose2: AppCompose = { runner: "docker-compose", services: [ @@ -123,7 +123,7 @@ describe('Deterministic JSON Serialization', () => { { port: 5432, name: "db" } ] } as AppCompose - + // Object keys should be sorted within arrays expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) @@ -135,7 +135,7 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", special_value: NaN } as AppCompose - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) // SHA256 hex length @@ -146,22 +146,22 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", special_value: Infinity } as AppCompose - + const compose2: AppCompose = { runner: "docker-compose", special_value: -Infinity } as AppCompose - + const compose3: AppCompose = { runner: "docker-compose", special_value: null } - + // All should produce the same hash since NaN and Infinity become null const hash1 = getComposeHash(compose1) const hash2 = getComposeHash(compose2) const hash3 = getComposeHash(compose3) - + expect(hash1).toBe(hash2) expect(hash2).toBe(hash3) }) @@ -171,11 +171,11 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", optional_field: undefined } as AppCompose - + const compose2: AppCompose = { runner: "docker-compose" } - + // undefined values should be treated consistently expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) @@ -188,15 +188,15 @@ describe('Deterministic JSON Serialization', () => { bash_script: "start.sh", docker_compose_file: "docker-compose.yml" } - + const hash = getComposeHash(compose, true) - + // Should be the same as compose without docker_compose_file const compose2: AppCompose = { runner: "bash", bash_script: "start.sh" } - + expect(hash).toBe(getComposeHash(compose2, true)) }) @@ -206,15 +206,15 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", bash_script: "start.sh" } - + const hash = getComposeHash(compose, true) - + // Should be the same as compose without bash_script const compose2: AppCompose = { runner: "docker-compose", docker_compose_file: "docker-compose.yml" } - + expect(hash).toBe(getComposeHash(compose2, true)) }) @@ -224,12 +224,12 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", pre_launch_script: "" } - + const compose2: AppCompose = { runner: "docker-compose", docker_compose_file: "docker-compose.yml" } - + expect(getComposeHash(compose1, true)).toBe(getComposeHash(compose2, true)) }) @@ -239,12 +239,12 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", pre_launch_script: "echo 'Starting...'" } - + const compose2: AppCompose = { runner: "docker-compose", docker_compose_file: "docker-compose.yml" } - + expect(getComposeHash(compose1, true)).not.toBe(getComposeHash(compose2, true)) }) }) @@ -256,13 +256,13 @@ describe('Deterministic JSON Serialization', () => { text: "你好世界", description: "🚀 Deploy" } as AppCompose - + const compose2: AppCompose = { description: "🚀 Deploy", runner: "docker-compose", text: "你好世界" } as AppCompose - + expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) }) @@ -284,13 +284,13 @@ describe('Deterministic JSON Serialization', () => { alpha: "first" } } as AppCompose - + const hash = getComposeHash(compose) - + // This should be a deterministic hash expect(hash).toHaveLength(64) expect(hash).toMatch(/^[a-f0-9]{64}$/) - + // The exact hash value depends on the specific data structure // but it should be consistent across runs const hash2 = getComposeHash(compose) @@ -303,7 +303,7 @@ describe('Deterministic JSON Serialization', () => { // @ts-expect-error - empty object is valid const compose: AppCompose = {} const hash = getComposeHash(compose) - + expect(hash).toHaveLength(64) expect(hash).toMatch(/^[a-f0-9]{64}$/) }) @@ -313,7 +313,7 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", optional_field: null } as AppCompose - + const hash = getComposeHash(compose) expect(hash).toBeDefined() }) @@ -324,13 +324,13 @@ describe('Deterministic JSON Serialization', () => { enabled: true, debug: false } as AppCompose - + const compose2: AppCompose = { debug: false, enabled: true, runner: "docker-compose" } as AppCompose - + expect(getComposeHash(compose1)).toBe(getComposeHash(compose2)) }) @@ -342,7 +342,7 @@ describe('Deterministic JSON Serialization', () => { float: 3.14159, large: 1e10 } as AppCompose - + const hash = getComposeHash(compose) expect(hash).toBeDefined() }) @@ -359,9 +359,9 @@ describe('Deterministic JSON Serialization', () => { DATABASE_URL: "postgres://localhost:5432/mydb" } } as AppCompose - + const hashes = Array.from({ length: 10 }, () => getComposeHash(compose)) - + // All hashes should be identical const firstHash = hashes[0] expect(hashes.every(hash => hash === firstHash)).toBe(true) @@ -372,12 +372,12 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", docker_compose_file: "docker-compose.yml" } - + const compose2: AppCompose = { runner: "bash", bash_script: "start.sh" } - + expect(getComposeHash(compose1)).not.toBe(getComposeHash(compose2)) }) }) @@ -390,7 +390,7 @@ describe('Deterministic JSON Serialization', () => { runner: "docker-compose", docker_compose_file: "docker-compose.yml" } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -408,7 +408,7 @@ describe('Deterministic JSON Serialization', () => { token_key: "token123" } } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -429,7 +429,7 @@ describe('Deterministic JSON Serialization', () => { no_instance_id: false, secure_time: true } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -444,7 +444,7 @@ describe('Deterministic JSON Serialization', () => { key_provider: "kms", key_provider_id: "abcd1234" } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -458,7 +458,7 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", allowed_envs: ["NODE_ENV", "PORT", "DATABASE_URL"] } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -472,7 +472,7 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", features: ["feature1", "feature2"] } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -486,10 +486,10 @@ describe('Deterministic JSON Serialization', () => { bash_script: "start.sh", docker_compose_file: "docker-compose.yml" } - + const hashWithoutNormalize = getComposeHash(compose) const hashExplicitFalse = getComposeHash(compose, false) - + expect(hashWithoutNormalize).toBe(hashExplicitFalse) }) @@ -499,10 +499,10 @@ describe('Deterministic JSON Serialization', () => { bash_script: "start.sh", docker_compose_file: "docker-compose.yml" } - + const hashWithNormalize = getComposeHash(compose, true) const hashWithoutNormalize = getComposeHash(compose, false) - + // These should be different because preprocessing is applied only with normalize=true expect(hashWithNormalize).not.toBe(hashWithoutNormalize) }) @@ -513,15 +513,15 @@ describe('Deterministic JSON Serialization', () => { docker_compose_file: "docker-compose.yml", pre_launch_script: "" } - + const composeWithoutEmpty: AppCompose = { runner: "docker-compose", docker_compose_file: "docker-compose.yml" } - + // With normalization, empty pre_launch_script should be removed expect(getComposeHash(compose, true)).toBe(getComposeHash(composeWithoutEmpty, true)) - + // Without normalization, empty pre_launch_script should remain expect(getComposeHash(compose, false)).not.toBe(getComposeHash(composeWithoutEmpty, false)) }) @@ -534,7 +534,7 @@ describe('Deterministic JSON Serialization', () => { bash_script: "start.sh", pre_launch_script: "echo 'Starting...'" } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) @@ -553,10 +553,10 @@ describe('Deterministic JSON Serialization', () => { allowed_envs: ["NODE_ENV"], secure_time: false } - + const hash = getComposeHash(compose) expect(hash).toBeDefined() expect(hash).toHaveLength(64) }) }) -}) \ No newline at end of file +}) diff --git a/sdk/js/src/__tests__/index.test.ts b/sdk/js/src/__tests__/index.test.ts index 66b6aeb9e..c1e4564c8 100644 --- a/sdk/js/src/__tests__/index.test.ts +++ b/sdk/js/src/__tests__/index.test.ts @@ -151,9 +151,9 @@ describe('DstackClient', () => { // Temporarily remove environment variable to test file check const savedEnv = process.env.DSTACK_SIMULATOR_ENDPOINT delete process.env.DSTACK_SIMULATOR_ENDPOINT - + expect(() => new DstackClient('/non/existent/socket')).toThrow('Unix socket file /non/existent/socket does not exist') - + // Restore environment variable if (savedEnv) { process.env.DSTACK_SIMULATOR_ENDPOINT = savedEnv @@ -164,10 +164,10 @@ describe('DstackClient', () => { // Temporarily remove environment variable to test non-unix socket paths const savedEnv = process.env.DSTACK_SIMULATOR_ENDPOINT delete process.env.DSTACK_SIMULATOR_ENDPOINT - + expect(() => new DstackClient('http://localhost:8080')).not.toThrow() expect(() => new DstackClient('https://example.com')).not.toThrow() - + // Restore environment variable if (savedEnv) { process.env.DSTACK_SIMULATOR_ENDPOINT = savedEnv @@ -281,36 +281,36 @@ describe('DstackClient', () => { it('should support deprecated deriveKey method with warning', async () => { const client = new TappdClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + const result = await client.deriveKey('/', 'test') expect(result).toHaveProperty('key') expect(result).toHaveProperty('certificate_chain') expect(consoleSpy).toHaveBeenCalledWith('deriveKey is deprecated, please use getKey instead') - + consoleSpy.mockRestore() }) it('should support deprecated tdxQuote method with warning', async () => { const client = new TappdClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + const result = await client.tdxQuote('test data') expect(result).toHaveProperty('quote') expect(result).toHaveProperty('event_log') expect(consoleSpy).toHaveBeenCalledWith('tdxQuote is deprecated, please use getQuote instead') - + consoleSpy.mockRestore() }) it('should support tdxQuote with hash algorithm parameter', async () => { const client = new TappdClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + const result = await client.tdxQuote('test data', 'sha256') expect(result).toHaveProperty('quote') expect(result).toHaveProperty('event_log') expect(consoleSpy).toHaveBeenCalledWith('tdxQuote is deprecated, please use getQuote instead') - + consoleSpy.mockRestore() }) }) @@ -319,16 +319,16 @@ describe('DstackClient', () => { it('should throws error in deriveKey method', async () => { const client = new DstackClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + await expect(() => client.deriveKey('/', 'test')).rejects.toThrow('deriveKey is deprecated, please use getKey instead.') - + consoleSpy.mockRestore() }) it('should throws error in tdxQuote method without hash algorithm parameter', async () => { const client = new DstackClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + await expect(() => client.tdxQuote('test data')).rejects.toThrow('tdxQuote only supports raw hash algorithm.') consoleSpy.mockRestore() @@ -337,7 +337,7 @@ describe('DstackClient', () => { it("should throws error in tdxQuote method with hash algorithm parameter other than raw", async () => { const client = new DstackClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + await expect(() => client.tdxQuote('test data', 'sha256')).rejects.toThrow('tdxQuote only supports raw hash algorithm.') consoleSpy.mockRestore() @@ -346,7 +346,7 @@ describe('DstackClient', () => { it('should able to get quote with plain report_data in tdxQuote method with warning', async () => { const client = new DstackClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + const result = await client.tdxQuote('test data', "raw") expect(result).toHaveProperty('quote') expect(result).toHaveProperty('event_log') @@ -358,9 +358,9 @@ describe('DstackClient', () => { it('should throws error in tdxQuote with hash algorithm parameter', async () => { const client = new DstackClient() const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) - + await expect(() => client.tdxQuote('test data', 'sha256')).rejects.toThrow('tdxQuote only supports raw hash algorithm.') - + consoleSpy.mockRestore() }) }) diff --git a/sdk/js/src/__tests__/send-rpc-request.test.ts b/sdk/js/src/__tests__/send-rpc-request.test.ts index f4936ca0b..aa5a7de8e 100644 --- a/sdk/js/src/__tests__/send-rpc-request.test.ts +++ b/sdk/js/src/__tests__/send-rpc-request.test.ts @@ -70,14 +70,14 @@ describe('send_rpc_request', () => { mockHttpRequest.mockImplementation((url, options, callback) => { // Call the callback with mock response callback(mockRes) - + // Setup response data handling const dataCallback = mockRes.on.mock.calls.find(call => call[0] === 'data')?.[1] const endCallback = mockRes.on.mock.calls.find(call => call[0] === 'end')?.[1] - + if (dataCallback) dataCallback('{"result": "success"}') if (endCallback) endCallback() - + return mockReq }) @@ -109,13 +109,13 @@ describe('send_rpc_request', () => { // Mock the request flow mockHttpsRequest.mockImplementation((url, options, callback) => { callback(mockRes) - + const dataCallback = mockRes.on.mock.calls.find(call => call[0] === 'data')?.[1] const endCallback = mockRes.on.mock.calls.find(call => call[0] === 'end')?.[1] - + if (dataCallback) dataCallback('{"result": "success"}') if (endCallback) endCallback() - + return mockReq }) @@ -150,13 +150,13 @@ describe('send_rpc_request', () => { mockHttpRequest.mockImplementation((url, options, callback) => { callback(mockRes) - + const dataCallback = mockRes.on.mock.calls.find(call => call[0] === 'data')?.[1] const endCallback = mockRes.on.mock.calls.find(call => call[0] === 'end')?.[1] - + if (dataCallback) dataCallback('invalid json') if (endCallback) endCallback() - + return mockReq }) @@ -246,7 +246,7 @@ describe('send_rpc_request', () => { const path = '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/api/test' const payload = '{"test": "data"}' - // Mock real setTimeout to trigger timeout immediately + // Mock real setTimeout to trigger timeout immediately const originalSetTimeout = global.setTimeout // @ts-ignore global.setTimeout = vi.fn((callback, delay) => { @@ -264,7 +264,7 @@ describe('send_rpc_request', () => { }) await expect(send_rpc_request(endpoint, path, payload, 1)).rejects.toThrow('request timed out') - + global.setTimeout = originalSetTimeout }) }) @@ -277,13 +277,13 @@ describe('send_rpc_request', () => { mockHttpRequest.mockImplementation((url, options, callback) => { callback(mockRes) - + const dataCallback = mockRes.on.mock.calls.find(call => call[0] === 'data')?.[1] const endCallback = mockRes.on.mock.calls.find(call => call[0] === 'end')?.[1] - + if (dataCallback) dataCallback('{"result": "success"}') if (endCallback) endCallback() - + return mockReq }) @@ -300,11 +300,11 @@ describe('send_rpc_request', () => { mockHttpRequest.mockImplementation((url, options, callback) => { callback(mockRes) - + // Setup multiple data and end events const dataCallback = mockRes.on.mock.calls.find(call => call[0] === 'data')?.[1] const endCallback = mockRes.on.mock.calls.find(call => call[0] === 'end')?.[1] - + setTimeout(() => { if (dataCallback) dataCallback('{"result": "success"}') if (endCallback) { @@ -312,7 +312,7 @@ describe('send_rpc_request', () => { endCallback() // Second end - should be ignored } }, 10) - + return mockReq }) @@ -320,4 +320,4 @@ describe('send_rpc_request', () => { expect(result).toEqual({ result: 'success' }) }) }) -}) \ No newline at end of file +}) diff --git a/sdk/js/src/send-rpc-request.ts b/sdk/js/src/send-rpc-request.ts index fa6837a14..e831c418f 100644 --- a/sdk/js/src/send-rpc-request.ts +++ b/sdk/js/src/send-rpc-request.ts @@ -13,21 +13,21 @@ export function send_rpc_request(endpoint: string, path: string, payloa return new Promise((resolve, reject) => { const abortController = new AbortController() let isCompleted = false - + const safeReject = (error: Error) => { if (!isCompleted) { isCompleted = true reject(error) } } - + const safeResolve = (result: T) => { if (!isCompleted) { isCompleted = true resolve(result) } } - + const timeout = setTimeout(() => { abortController.abort() safeReject(new Error('request timed out')) @@ -146,4 +146,4 @@ export function send_rpc_request(endpoint: string, path: string, payloa }) } }) -} \ No newline at end of file +} diff --git a/sdk/js/test-outputs.js b/sdk/js/test-outputs.js index 6c791319f..37aa18238 100644 --- a/sdk/js/test-outputs.js +++ b/sdk/js/test-outputs.js @@ -9,18 +9,18 @@ const { toKeypair, toKeypairSecure } = require('./dist/node/solana.js'); async function main() { console.log("=== JS SDK Output Test ==="); - + try { // Test client get_key const client = new DstackClient(); console.log("\n1. Testing DstackClient.getKey()"); - + const testPaths = [ { path: "test/wallet", purpose: "ethereum" }, { path: "test/signing", purpose: "solana" }, { path: "user/alice", purpose: "mainnet" } ]; - + for (const { path, purpose } of testPaths) { const keyResult = await client.getKey(path, purpose); console.log(`getKey('${path}', '${purpose}'):`); @@ -32,7 +32,7 @@ async function main() { // Test viem integration console.log("\n2. Testing Viem Integration"); const ethKey = await client.getKey("eth/test", "wallet"); - + console.log("\n2.1 toViemAccount (legacy):"); try { const account = toViemAccount(ethKey); @@ -41,7 +41,7 @@ async function main() { } catch (error) { console.log(` error: ${error.message}`); } - + console.log("\n2.2 toViemAccountSecure:"); try { const accountSecure = toViemAccountSecure(ethKey); @@ -54,7 +54,7 @@ async function main() { // Test solana integration console.log("\n3. Testing Solana Integration"); const solKey = await client.getKey("sol/test", "wallet"); - + console.log("\n3.1 toKeypair (legacy):"); try { const keypair = toKeypair(solKey); @@ -64,7 +64,7 @@ async function main() { } catch (error) { console.log(` error: ${error.message}`); } - + console.log("\n3.2 toKeypairSecure:"); try { const keypairSecure = toKeypairSecure(solKey); @@ -112,12 +112,12 @@ async function main() { }, { manifest_version: 1, - name: "another-app", + name: "another-app", runner: "docker-compose", docker_compose_file: "services:\\n web:\\n build: .\\n environment:\\n - NODE_ENV=production" } ]; - + testComposes.forEach((compose, index) => { const hash = getComposeHash(compose); console.log(`compose ${index + 1}: ${hash}`); @@ -155,4 +155,4 @@ async function main() { } } -main().catch(console.error); \ No newline at end of file +main().catch(console.error); diff --git a/sdk/python/Makefile b/sdk/python/Makefile index 88d462327..801027866 100644 --- a/sdk/python/Makefile +++ b/sdk/python/Makefile @@ -23,7 +23,7 @@ lint: type-check: pdm run mypy src/ -# Run type checking on tests specifically +# Run type checking on tests specifically type-check-tests: pdm run mypy tests/test_mypy_check.py tests/test_typing.py diff --git a/sdk/run-tests.sh b/sdk/run-tests.sh index 8ab258f2a..a16e4a3c5 100755 --- a/sdk/run-tests.sh +++ b/sdk/run-tests.sh @@ -7,11 +7,13 @@ set -Eeuo pipefail -ROOT_DIR="$(pwd -P)" +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" SIMULATOR_DIR="$ROOT_DIR/simulator" SIMULATOR_LOG="$SIMULATOR_DIR/dstack-simulator.log" DSTACK_SOCKET="$SIMULATOR_DIR/dstack.sock" TAPPD_SOCKET="$SIMULATOR_DIR/tappd.sock" +GUEST_SOCKET="$SIMULATOR_DIR/guest.sock" +EXTERNAL_SOCKET="$SIMULATOR_DIR/external.sock" SIMULATOR_PID="" cleanup() { @@ -19,6 +21,7 @@ cleanup() { kill "$SIMULATOR_PID" 2>/dev/null || true wait "$SIMULATOR_PID" 2>/dev/null || true fi + rm -f "$DSTACK_SOCKET" "$TAPPD_SOCKET" "$GUEST_SOCKET" "$EXTERNAL_SOCKET" } print_simulator_logs() { @@ -52,7 +55,12 @@ wait_for_socket() { trap 'print_simulator_logs' ERR trap cleanup EXIT INT TERM -rm -f "$DSTACK_SOCKET" "$TAPPD_SOCKET" "$SIMULATOR_LOG" +rm -f \ + "$DSTACK_SOCKET" \ + "$TAPPD_SOCKET" \ + "$GUEST_SOCKET" \ + "$EXTERNAL_SOCKET" \ + "$SIMULATOR_LOG" export DSTACK_SIMULATOR_ENDPOINT="$DSTACK_SOCKET" export TAPPD_SIMULATOR_ENDPOINT="$TAPPD_SOCKET" @@ -70,20 +78,20 @@ SIMULATOR_PID=$! wait_for_socket "$DSTACK_SOCKET" "dstack" wait_for_socket "$TAPPD_SOCKET" "tappd" -pushd rust/ +pushd "$ROOT_DIR/rust" cargo test -- --show-output cargo run --example tappd_client_usage cargo run --example dstack_client_usage cargo test -p dstack-sdk-types --test no_std_test --no-default-features popd -pushd go/ +pushd "$ROOT_DIR/go" go clean -testcache go test -v ./dstack DSTACK_SIMULATOR_ENDPOINT=$TAPPD_SIMULATOR_ENDPOINT go test -v ./tappd popd -pushd python/ +pushd "$ROOT_DIR/python" # Ensure PDM is installed if ! command -v pdm &> /dev/null; then echo "Installing PDM..." @@ -97,7 +105,7 @@ pdm run test pdm run check popd -pushd js/ +pushd "$ROOT_DIR/js" npm install npm run test -- --run popd diff --git a/sdk/rust/.gitignore b/sdk/rust/.gitignore index a6f89c2da..b83d22266 100644 --- a/sdk/rust/.gitignore +++ b/sdk/rust/.gitignore @@ -1 +1 @@ -/target/ \ No newline at end of file +/target/ diff --git a/sdk/rust/Cargo.toml b/sdk/rust/Cargo.toml index b3bb3d398..f40c1a721 100644 --- a/sdk/rust/Cargo.toml +++ b/sdk/rust/Cargo.toml @@ -6,6 +6,7 @@ # SPDX-License-Identifier: Apache-2.0 [package] +workspace = "../../dstack" name = "dstack-sdk" version = "0.1.3" edition = "2021" diff --git a/sdk/rust/types/Cargo.toml b/sdk/rust/types/Cargo.toml index 2bedc93ad..86a06a468 100644 --- a/sdk/rust/types/Cargo.toml +++ b/sdk/rust/types/Cargo.toml @@ -4,6 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 [package] +workspace = "../../../dstack" name = "dstack-sdk-types" version = "0.1.3" edition = "2021" diff --git a/sdk/simulator/.gitignore b/sdk/simulator/.gitignore index 1acd17bdb..fd1ce3bc3 100644 --- a/sdk/simulator/.gitignore +++ b/sdk/simulator/.gitignore @@ -1,3 +1,4 @@ dstack-simulator dstack-guest-agent *.lock +*.log diff --git a/sdk/simulator/app-compose.json b/sdk/simulator/app-compose.json index bcbba37d3..8fb0d5be6 100644 --- a/sdk/simulator/app-compose.json +++ b/sdk/simulator/app-compose.json @@ -1 +1 @@ -{"manifest_version":2,"name":"guest-agent","runner":"docker-compose","docker_compose_file":"services:\n dstack-agent:\n image: ubuntu\n user: root\n network_mode: host\n volumes:\n - /:/host/\n - /var/run/tappd.sock:/var/run/tappd.sock\n - /var/run/dstack.sock:/var/run/dstack.sock\n entrypoint: |\n bash -c '\n apt-get update && apt-get install -y socat\n socat TCP-LISTEN:2000,fork UNIX-CONNECT:/var/run/tappd.sock &\n socat TCP-LISTEN:3000,fork UNIX-CONNECT:/var/run/dstack.sock &\n tail -f /dev/null\n '\n dstack-verifier:\n image: dstacktee/dstack-verifier:0.5.4\n ports:\n - \"8080:8080\"\n restart: unless-stopped","gateway_enabled":true,"public_logs":true,"public_sysinfo":true,"public_tcbinfo":true,"key_provider_id":"","allowed_envs":[],"no_instance_id":false,"secure_time":false,"key_provider":"kms","kms_enabled":true,"storage_fs":"ext4","pre_launch_script":"docker run --rm --privileged --pid=host --net=host -v /:/host \\\n -e SSH_GITHUB_USER=\"kvinwang\" \\\n kvin/dstack-openssh-installer:latest"} \ No newline at end of file +{"manifest_version":2,"name":"guest-agent","runner":"docker-compose","docker_compose_file":"services:\n dstack-agent:\n image: ubuntu\n user: root\n network_mode: host\n volumes:\n - /:/host/\n - /var/run/tappd.sock:/var/run/tappd.sock\n - /var/run/dstack.sock:/var/run/dstack.sock\n entrypoint: |\n bash -c '\n apt-get update && apt-get install -y socat\n socat TCP-LISTEN:2000,fork UNIX-CONNECT:/var/run/tappd.sock &\n socat TCP-LISTEN:3000,fork UNIX-CONNECT:/var/run/dstack.sock &\n tail -f /dev/null\n '\n dstack-verifier:\n image: dstacktee/dstack-verifier:0.5.4\n ports:\n - \"8080:8080\"\n restart: unless-stopped","gateway_enabled":true,"public_logs":true,"public_sysinfo":true,"public_tcbinfo":true,"key_provider_id":"","allowed_envs":[],"no_instance_id":false,"secure_time":false,"key_provider":"kms","kms_enabled":true,"storage_fs":"ext4","pre_launch_script":"docker run --rm --privileged --pid=host --net=host -v /:/host \\\n -e SSH_GITHUB_USER=\"kvinwang\" \\\n kvin/dstack-openssh-installer:latest"} diff --git a/sdk/simulator/appkeys.json b/sdk/simulator/appkeys.json index 1e67f019d..e564286cd 100644 --- a/sdk/simulator/appkeys.json +++ b/sdk/simulator/appkeys.json @@ -10,4 +10,4 @@ "key": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg1PYCFKYfDmUfv5fk\nstppasf4mPGqnz0fEoLEnGx8CnKhRANCAAQZskV92biqZEbZgPE2//eBLs1sd3QO\n6w4osQ1WwMAAKKW25Z3OowfVg70fcHaenBFt8rz7F1ipXYYT594chILA\n-----END PRIVATE KEY-----\n" } } -} \ No newline at end of file +} diff --git a/sdk/simulator/build.sh b/sdk/simulator/build.sh index fca993175..8b81c0fd2 100755 --- a/sdk/simulator/build.sh +++ b/sdk/simulator/build.sh @@ -4,7 +4,13 @@ # # SPDX-License-Identifier: Apache-2.0 -cd $(dirname $0) -cargo build --release -p dstack-guest-agent-simulator -cp ../../target/release/dstack-simulator . +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cargo build \ + --manifest-path "$REPO_ROOT/dstack/Cargo.toml" \ + --release \ + -p dstack-guest-agent-simulator +cp "$REPO_ROOT/dstack/target/release/dstack-simulator" "$SCRIPT_DIR/" diff --git a/sdk/simulator/sys-config.json b/sdk/simulator/sys-config.json index 1b2d5b48d..4993438b3 100644 --- a/sdk/simulator/sys-config.json +++ b/sdk/simulator/sys-config.json @@ -9,4 +9,4 @@ "docker_registry": "", "host_api_url": "vsock://2:12000/api", "vm_config": "{\"os_image_hash\":\"64f0d1545cd510a8dfed7ad609d105b5d41f0cb2afcfdda8867ede00c88add7a\",\"cpu_count\":1,\"memory_size\":2147483648}" -} \ No newline at end of file +} diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 000000000..87a2704cb --- /dev/null +++ b/tools/README.md @@ -0,0 +1,16 @@ +# Repository tools + +This directory contains developer and operator utilities that do not belong to +one runtime component or one guest-OS backend. + +- `add-spdx-attribution.py` updates SPDX attribution metadata. +- `mock-cf-dns-api/` provides a local Cloudflare DNS API test double. +- `sca/` builds self-contained application images. +- `dev-stack.sh` preserves the unsupported legacy all-in-one host, guest, and + deployment helper. +- `vm-runner/` preserves the unsupported legacy direct-QEMU runner and GPU + passthrough helper. + +The legacy tools are kept out of `os/yocto/` because they cross host, guest, +and deployment boundaries. Use the Rust `dstack` and `dstackup` CLIs for +supported workflows. diff --git a/scripts/add-spdx-attribution.py b/tools/add-spdx-attribution.py similarity index 100% rename from scripts/add-spdx-attribution.py rename to tools/add-spdx-attribution.py diff --git a/tools/dev-stack.sh b/tools/dev-stack.sh new file mode 100755 index 000000000..af43e8351 --- /dev/null +++ b/tools/dev-stack.sh @@ -0,0 +1,413 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +# Legacy all-in-one host development helper. It is retained outside both the +# supported dstack CLI and the Yocto backend because it crosses host build, +# guest build, and deployment/configuration boundaries. +TOOL_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(realpath "$TOOL_DIR/..") +ACTION=$1 + +YOCTO_DIR=$REPO_ROOT/os/yocto +DSTACK_DIR=$REPO_ROOT/dstack +CERTS_DIR=$(pwd)/certs +IMAGES_DIR=$(pwd)/images +RUN_DIR=$(pwd)/run +RUST_BUILD_DIR=$(pwd)/rust-target +CERBOT_WORKDIR=$RUN_DIR/certbot +KMS_UPGRADE_REGISTRY_DIR=$RUN_DIR/kms/upgrade_registry +KMS_CERT_LOG_DIR=$RUN_DIR/kms/cert_log/ + +GATEWAY_CERT=${GATEWAY_CERT:-$CERTS_DIR/live/cert.pem} +GATEWAY_KEY=${GATEWAY_KEY:-$CERTS_DIR/live/key.pem} + +CONFIG_FILE=./build-config.sh + +check_config() { + local template_file=$1 + local config_file=$2 + + # extract all variables in template file + local variables=$(grep -oE '^\s*[A-Z_]+=' $template_file | sort) + + # check if each variable is set in config file + local var missing=0 + for var in $variables; do + if ! grep -qE "^\s*$var" $config_file; then + echo "Variable $var is not set in $config_file" + missing=1 + fi + done + if [ $missing -ne 0 ]; then + return 1 + fi + return 0 +} + +require_config() { + + # Base port for RPC services + BASE_PORT=$(($RANDOM % 1000 * 10 + 10000)) + CID_POOL_START=$(($RANDOM % 1000 * 1000 + 20000)) + SUBNET_INDEX=$(($RANDOM % 240 + 10)) + + cat <build-config.sh.tpl +# DNS domain of kms rpc and dstack-gateway rpc +# *.1022.dstack.org resolves to 10.0.2.2 which is the IP of the host system +# from CVMs point of view +KMS_DOMAIN=kms.1022.dstack.org +GATEWAY_DOMAIN=gateway.1022.dstack.org + +# CIDs allocated to VMs start from this number of type unsigned int32 +VMM_CID_POOL_START=$CID_POOL_START +# CID pool size +VMM_CID_POOL_SIZE=1000 + +VMM_RPC_LISTEN_PORT=$BASE_PORT +# Whether port mapping from host to CVM is allowed +VMM_PORT_MAPPING_ENABLED=true +# Host API configuration, type of uint32 +VMM_VSOCK_LISTEN_PORT=$BASE_PORT +# Whether to enable GPU support +VMM_ENABLE_GPU=false + +KMS_RPC_LISTEN_PORT=$(($BASE_PORT + 1)) +GATEWAY_RPC_LISTEN_PORT=$(($BASE_PORT + 2)) + +GATEWAY_WG_INTERFACE=dgw-$USER +GATEWAY_WG_LISTEN_PORT=$(($BASE_PORT + 3)) +GATEWAY_WG_IP=10.$SUBNET_INDEX.3.1 +GATEWAY_SERVE_PORT=$(($BASE_PORT + 4)) +GATEWAY_CERT=$CERBOT_WORKDIR/live/cert.pem +GATEWAY_KEY=$CERBOT_WORKDIR/live/key.pem + +BIND_PUBLIC_IP=0.0.0.0 + +GATEWAY_PUBLIC_DOMAIN= + +# for certbot +CERTBOT_ENABLED=false +CF_API_TOKEN= +ACME_URL=https://acme-staging-v02.api.letsencrypt.org/directory +EOF + if [ -f $CONFIG_FILE ]; then + source $CONFIG_FILE + # check if any variable in build-config.sh.tpl is not set in build-config.sh. + # This might occur if the build-config.sh is generated from and old repo. + check_config build-config.sh.tpl $CONFIG_FILE + if [ $? -ne 0 ]; then + exit 1 + fi + rm -f build-config.sh.tpl + + if [ -z "$GATEWAY_SERVE_PORT" ]; then + GATEWAY_SERVE_PORT=${GATEWAY_LISTEN_PORT1} + fi + AGENT_PORT=8090 + else + mv build-config.sh.tpl $CONFIG_FILE + echo "Config file $CONFIG_FILE created, please edit it to configure the build" + exit 1 + fi +} + +# Step 1: build binaries +build_host() { + echo "Building binaries" + (cd $DSTACK_DIR && cargo build --release --target-dir ${RUST_BUILD_DIR}) + for bin in dstack-gateway dstack-kms dstack-vmm supervisor; do + cp "${RUST_BUILD_DIR}/release/${bin}" ".${bin}.new" + mv -f ".${bin}.new" "./${bin}" + done +} + +# Step 2: build guest images +build_guest() { + echo "Building guest images" + if [ -z "$BBPATH" ]; then + source "$YOCTO_DIR/dev-setup" "$1" + fi + make -C "$YOCTO_DIR" dist DIST_DIR=$IMAGES_DIR BB_BUILD_DIR=${BBPATH} +} + +# Step 4: generate config files + +build_cfg() { + echo "Building config files" + if [ -f "gateway.toml" ]; then + echo "Reading existing WireGuard key from gateway.toml" + GATEWAY_WG_KEY=$(awk ' + /^\s*private_key\s*=/ { + # Remove leading whitespace and "private_key =" + gsub(/^\s*private_key\s*=\s*/, "") + # Remove quotes (both single and double) + gsub(/^["'"'"']|["'"'"']$/, "") + # Remove trailing whitespace and comments + gsub(/\s*(#.*)?$/, "") + if (length($0) > 0) { + print $0 + exit + } + } + ' gateway.toml) + + if [ -z "$GATEWAY_WG_KEY" ]; then + echo "Error: Could not read WireGuard key from existing gateway.toml" + exit 1 + fi + else + echo "Generating new WireGuard key" + GATEWAY_WG_KEY=$(wg genkey) + fi + + GATEWAY_WG_PUBKEY=$(echo $GATEWAY_WG_KEY | wg pubkey) + # kms + cat <kms.toml +log_level = "info" + +[rpc] +address = "127.0.0.1" +port = $KMS_RPC_LISTEN_PORT + +[rpc.tls] +key = "$CERTS_DIR/rpc.key" +certs = "$CERTS_DIR/rpc.crt" + +[rpc.tls.mutual] +ca_certs = "$CERTS_DIR/tmp-ca.crt" +mandatory = false + +[core] +cert_dir = "$CERTS_DIR" + +[core.gpu] +enabled = $VMM_ENABLE_GPU + +[core.auth_api] +type = "dev" + +[core.onboard] +quote_enabled = false +address = "127.0.0.1" +port = $KMS_RPC_LISTEN_PORT +auto_bootstrap_domain = "$KMS_DOMAIN" + +[core.image] +verify = false +EOF + + # dstack-gateway + cat <gateway.toml +log_level = "info" +address = "127.0.0.1" +port = $GATEWAY_RPC_LISTEN_PORT + +[tls] +key = "$CERTS_DIR/gateway-rpc.key" +certs = "$CERTS_DIR/gateway-rpc.cert" + +[tls.mutual] +ca_certs = "$CERTS_DIR/gateway-ca.cert" +mandatory = false + +[core] +kms_url = "https://localhost:$KMS_RPC_LISTEN_PORT" +rpc_domain = "$GATEWAY_DOMAIN" +run_in_dstack = false + +[core.sync] +enabled = false + +[core.certbot] +enabled = $CERTBOT_ENABLED +# Path to the working directory +workdir = "$CERBOT_WORKDIR" +# ACME server URL +acme_url = "$ACME_URL" +# Cloudflare API token +cf_api_token = "$CF_API_TOKEN" +# Auto set CAA record +auto_set_caa = true +# Domain to issue certificates for +domain = "*.$GATEWAY_PUBLIC_DOMAIN" +# Check renewal interval +renew_interval = "30m" +# Number of days before expiration to trigger renewal +renew_days_before = "10d" +# Renew timeout +renew_timeout = "10m" + +[core.wg] +private_key = "$GATEWAY_WG_KEY" +public_key = "$GATEWAY_WG_PUBKEY" +listen_port = $GATEWAY_WG_LISTEN_PORT +ip = "$GATEWAY_WG_IP/24" +reserved_net = ["$GATEWAY_WG_IP/31"] +client_ip_range = "$GATEWAY_WG_IP/24" +config_path = "$RUN_DIR/wg.conf" +interface = "$GATEWAY_WG_INTERFACE" +endpoint = "10.0.2.2:$GATEWAY_WG_LISTEN_PORT" + +[core.proxy] +cert_chain = "$GATEWAY_CERT" +cert_key = "$GATEWAY_KEY" +base_domain = "$GATEWAY_PUBLIC_DOMAIN" +listen_addr = "$BIND_PUBLIC_IP" +listen_port = $GATEWAY_SERVE_PORT +agent_port = $AGENT_PORT +app_address_ns_prefix = "_tapp-address" +EOF + + # dstack-vmm config + cat <vmm.toml +log_level = "info" +address = "127.0.0.1" +port = $VMM_RPC_LISTEN_PORT +image_path = "$IMAGES_DIR" +run_path = "$RUN_DIR/vm" +kms_url = "https://localhost:$KMS_RPC_LISTEN_PORT" + +[cvm] +kms_urls = ["https://$KMS_DOMAIN:$KMS_RPC_LISTEN_PORT"] +gateway_urls = ["https://$GATEWAY_DOMAIN:$GATEWAY_RPC_LISTEN_PORT"] +cid_start = $VMM_CID_POOL_START +cid_pool_size = $VMM_CID_POOL_SIZE +[cvm.port_mapping] +enabled = $VMM_PORT_MAPPING_ENABLED +address = "127.0.0.1" +range = [ + { protocol = "tcp", from = 1, to = 20000 }, + { protocol = "udp", from = 1, to = 20000 }, +] + +[gateway] +base_domain = "$GATEWAY_PUBLIC_DOMAIN" +port = $GATEWAY_SERVE_PORT +agent_port = $AGENT_PORT + +[host_api] +port = $VMM_VSOCK_LISTEN_PORT +EOF + + mkdir -p $RUN_DIR + mkdir -p $CERBOT_WORKDIR/backup/preinstalled +} + +guest_os_release_url() { + local VERSION=$1 + local BASENAME=$2 + + if [[ ! "$VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)([-.][0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid guest OS version: $VERSION" >&2 + return 1 + fi + + local MAJOR=$((10#${BASH_REMATCH[1]})) + local MINOR=$((10#${BASH_REMATCH[2]})) + if (( MAJOR == 0 && MINOR < 6 )); then + echo "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/Dstack-TEE/meta-dstack/releases/download/v$VERSION/$BASENAME.tar.gz" + else + echo "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/Dstack-TEE/dstack/releases/download/guest-os-v$VERSION/$BASENAME.tar.gz" + fi +} + +download_image() { + local VERSION="" + local IS_DEV="" + + # Parse arguments to support both formats + if [[ "$1" == "-dev" ]]; then + IS_DEV=1 + VERSION=$2 + else + VERSION=$1 + fi + + echo "Downloading image $VERSION${IS_DEV:+ (dev)}" + + if [ x"$IS_DEV" = x"1" ]; then + BASENAME=dstack-dev-$VERSION + else + BASENAME=dstack-$VERSION + fi + if ! URL=$(guest_os_release_url "$VERSION" "$BASENAME"); then + return 1 + fi + if [ -d $IMAGES_DIR/$BASENAME ]; then + echo "Image already exists" + else + mkdir -p $IMAGES_DIR/$BASENAME.tmp + curl -fL "$URL" -o $IMAGES_DIR/$BASENAME.tar.gz + tar -xvf $IMAGES_DIR/$BASENAME.tar.gz -C $IMAGES_DIR/$BASENAME.tmp + rm -f $IMAGES_DIR/$BASENAME.tar.gz + if [ -d $IMAGES_DIR/$BASENAME.tmp/$BASENAME ]; then + mv $IMAGES_DIR/$BASENAME.tmp/$BASENAME $IMAGES_DIR/$BASENAME + rm -rf $IMAGES_DIR/$BASENAME.tmp + else + mv $IMAGES_DIR/$BASENAME.tmp $IMAGES_DIR/$BASENAME + fi + fi +} + +usage() { + cat < [args] + +Actions: + host - Build host binaries only + image - Build guest images (backend interface) + guest - Alias for image + cfg - Generate configuration files only + dl - Download a specific image + hostcfg - Build host binaries and generate configuration files + all - Build everything (host, guest, and configuration) + help - Show this help + +Build a specific guest flavor (default builds production): set the FLAVORS env var. + flavor -> output image + prod -> dstack + dev -> dstack-dev + + Examples: + $0 guest # build dstack (default) + FLAVORS=dev $0 guest # build only dstack-dev + FLAVORS="prod dev" $0 guest # build both flavors +EOF +} + +case $ACTION in +host) + build_host + ;; +image|guest) + build_guest $2 + ;; +cfg) + require_config + build_cfg + ;; +dl) + download_image $2 $3 + ;; +hostcfg) + require_config + build_host + build_cfg + ;; +all) + require_config + build_host + build_guest + build_cfg + ;; +help|-h|--help) + usage + ;; +*) + [ -n "$ACTION" ] && echo "Invalid action: $ACTION" >&2 + usage + exit 1 + ;; +esac diff --git a/tools/vm-runner/README.md b/tools/vm-runner/README.md new file mode 100644 index 000000000..dc569fdbc --- /dev/null +++ b/tools/vm-runner/README.md @@ -0,0 +1,15 @@ +# Legacy direct-QEMU runner + +This directory contains the imported direct-QEMU development runner. It is +retained for low-level image debugging; normal deployments should use +`dstack-vmm`, `dstack`, or `dstackup`. + +Run it explicitly instead of adding the directory to `PATH`: + +```bash +python3 tools/vm-runner/vm-runner.py --help +``` + +`enable-vfio-passthrough.sh` is the matching standalone host helper for NVIDIA +GPU/NVSwitch passthrough. The `samples/` directory contains the historical CUDA +notebook workload used with this runner. diff --git a/tools/vm-runner/enable-vfio-passthrough.sh b/tools/vm-runner/enable-vfio-passthrough.sh new file mode 100755 index 000000000..b4259ad3b --- /dev/null +++ b/tools/vm-runner/enable-vfio-passthrough.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +echo "==> Detecting NVIDIA GPUs and NVSwitches" +mapfile -t GPU_BDFS < <( + lspci -d 10de: -nn | grep '3D controller' | awk '{print $1}' +) +mapfile -t GPU_DEV_IDS < <( + lspci -d 10de: -nn | grep '3D controller' | sed -n 's/.*\[10de:\([0-9A-Fa-f]\+\)\].*/\1/p' +) +mapfile -t SW_BDFS < <( + lspci -d 10de: -nn | grep 'Bridge' | awk '{print $1}' +) +mapfile -t SW_DEV_IDS < <( + lspci -d 10de: -nn | grep 'Bridge' | sed -n 's/.*\[10de:\([0-9A-Fa-f]\+\)\].*/\1/p' +) + +NGPU=${#GPU_BDFS[@]} +NSW=${#SW_BDFS[@]} +TOTAL=$((NGPU+NSW)) + +if [ $TOTAL -eq 0 ]; then + echo "No NVIDIA GPUs or NVSwitches found. Exiting." + exit 1 +fi + +echo "Found $NGPU GPU(s): ${GPU_BDFS[*]}" +echo "Found $NSW NVSwitch(s): ${SW_BDFS[*]}" + +echo "==> Loading VFIO modules" +sudo modprobe vfio vfio_pci + +echo "==> Tagging devices for VFIO passthrough" +# GPUs first +for idx in "${!GPU_BDFS[@]}"; do + BDF=${GPU_BDFS[$idx]}; DEV=${GPU_DEV_IDS[$idx]} + echo "Tagging GPU $BDF (DevID=10de:$DEV)" + if ! echo "10de $DEV" | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id > /dev/null; then + echo " -> failure, removing and retrying" + echo "10de $DEV" | sudo tee /sys/bus/pci/drivers/vfio-pci/remove_id > /dev/null || true + echo "10de $DEV" | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id > /dev/null + fi +done +# NVSwitches +for idx in "${!SW_BDFS[@]}"; do + BDF=${SW_BDFS[$idx]}; DEV=${SW_DEV_IDS[$idx]} + echo "Tagging NVSwitch $BDF (DevID=10de:$DEV)" + if ! echo "10de $DEV" | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id > /dev/null; then + echo " -> failure, removing and retrying" + echo "10de $DEV" | sudo tee /sys/bus/pci/drivers/vfio-pci/remove_id > /dev/null || true + echo "10de $DEV" | sudo tee /sys/bus/pci/drivers/vfio-pci/new_id > /dev/null + fi +done diff --git a/tools/vm-runner/host_api.py b/tools/vm-runner/host_api.py new file mode 100644 index 000000000..e1e870df3 --- /dev/null +++ b/tools/vm-runner/host_api.py @@ -0,0 +1,115 @@ +"""Minimal host API used by the legacy direct-QEMU development runner.""" + +import os +import json +import struct +import socket +import urllib +from dataclasses import dataclass +from http.server import HTTPServer, BaseHTTPRequestHandler + + +@dataclass +class ServerConfig: + kp_address: str + kp_port: int + vm_dir: str + + +class QuoteError(Exception): + pass + + +@dataclass +class QuoteResponse: + encrypted_key: bytes + provider_quote: bytes + + @classmethod + def from_json(cls, data: dict) -> 'QuoteResponse': + return cls( + encrypted_key=bytes(data['encrypted_key']), + provider_quote=bytes(data['provider_quote']) + ) + + +def get_key(quote: bytes, address: str, port: int) -> QuoteResponse: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((address, port)) + + try: + payload = {"quote": list(quote)} + serialized = json.dumps(payload).encode() + length = len(serialized) + + sock.sendall(struct.pack('>I', length)) + sock.sendall(serialized) + + response_length_bytes = sock.recv(4) + response_length = struct.unpack('>I', response_length_bytes)[0] + + response_data = b'' + while len(response_data) < response_length: + chunk = sock.recv(min(4096, response_length - len(response_data))) + if not chunk: + raise ConnectionError("Connection closed prematurely") + response_data += chunk + + response_json = json.loads(response_data) + return QuoteResponse.from_json(response_json) + + finally: + sock.close() + + +class QuoteHandler(BaseHTTPRequestHandler): + def __init__(self, config: ServerConfig, *args, **kwargs): + self.config = config + super().__init__(*args, **kwargs) + + def do_POST(self): + parsed_path = urllib.parse.urlparse(self.path) + + content_length = int(self.headers['Content-Length']) + if content_length > 1024 * 128: + self.respond(400, json.dumps({'error': 'Request body too large'}).encode()) + return + + body = self.rfile.read(content_length) + + match parsed_path.path: + case "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/api/GetSealingKey": + quote = json.loads(body) + response = get_key(bytes.fromhex(quote['quote']), self.config.kp_address, self.config.kp_port) + response_data = { + 'encrypted_key': response.encrypted_key.hex(), + 'provider_quote': response.provider_quote.hex() + } + response_bytes = json.dumps(response_data).encode() + case "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/api/Notify": + info = json.loads(body) + if info['event'] == 'instance.info': + info_path = os.path.join(self.config.vm_dir, 'shared', '.instance_info') + open(info_path, 'w').write(info['payload']) + response_bytes = b'null' + case _: + self.respond(404, b'null') + return + + self.respond(200, response_bytes) + + def respond(self, status: int, data: bytes): + self.send_response(status) + self.send_header('Content-Type', 'application/json') + self.send_header('Content-Length', len(data)) + self.end_headers() + self.wfile.write(data) + + +def create_http_server(config: ServerConfig): + def handler(*args): + QuoteHandler(config, *args) + + server = HTTPServer(('localhost', 0), handler) + chosen_port = server.server_port + return server, chosen_port diff --git a/tools/vm-runner/samples/Dockerfile b/tools/vm-runner/samples/Dockerfile new file mode 100644 index 000000000..0a916aa02 --- /dev/null +++ b/tools/vm-runner/samples/Dockerfile @@ -0,0 +1,42 @@ +FROM nvidia/cuda:12.4.0-devel-ubuntu20.04 + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC + +# Add deadsnakes PPA to get Python 3.10 +RUN apt-get update && apt-get install -y software-properties-common +RUN add-apt-repository ppa:deadsnakes/ppa + +# Install Python 3.10 and dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3.10 \ + python3.10-distutils \ + python3.10-venv \ + curl \ + ca-certificates \ + build-essential \ + cmake \ + git \ + libjpeg-dev \ + libpng-dev \ + vim \ + && rm -rf /var/lib/apt/lists/* + +# Update alternatives to use Python 3.10 +RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1 + +# Install pip for Python 3.10 +RUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10 + +# Install Python packages +RUN python3 -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 +RUN python3 -m pip install jupyterlab networkx six + +# Create a working directory +WORKDIR /workspace + +# Expose the port JupyterLab will be accessible on +EXPOSE 8888 + +# Command to start JupyterLab +CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root"] diff --git a/tools/vm-runner/samples/docker-compose.yaml b/tools/vm-runner/samples/docker-compose.yaml new file mode 100644 index 000000000..6eba3a293 --- /dev/null +++ b/tools/vm-runner/samples/docker-compose.yaml @@ -0,0 +1,15 @@ +services: + jupyter: + image: kvin/cuda-notebook + user: root + privileged: true + ports: + - "8888:8888" + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + runtime: nvidia diff --git a/tools/vm-runner/vm-runner.py b/tools/vm-runner/vm-runner.py new file mode 100755 index 000000000..7bce7fe4f --- /dev/null +++ b/tools/vm-runner/vm-runner.py @@ -0,0 +1,877 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) Hashforest Technology LLC +# +# SPDX-License-Identifier: Apache-2.0 + +"""Legacy direct-QEMU development runner for dstack guest OS images.""" + +import argparse +import json +import logging +import os +import random +import re +import string +import subprocess +import uuid +import configparser +import host_api +import threading +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import List, Dict, Optional +from functools import reduce + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def generate_config_paths(): + paths = [ + "/etc/dstack/client.conf", + os.path.expanduser("~/.config/dstack/client.conf"), + ] + current_dir = os.getcwd() + while current_dir != "/": + paths.append(os.path.join(current_dir, ".dstack", "client.conf")) + current_dir = os.path.dirname(current_dir) + return paths + + +def merge2(a, b): + if isinstance(a, dict) and isinstance(b, dict): + c = a.copy() + for k, v in b.items(): + c[k] = merge2(a.get(k), v) + return c + if b is None: + return a + return b + + +def test_merge2(): + assert merge2({"a": 1}, {"b": 2}) == {"a": 1, "b": 2} + assert merge2({"a": 1}, {"a": 2}) == {"a": 2} + assert merge2({"a": {"b": 1}}, {"a": {"c": 2}}) == {"a": {"b": 1, "c": 2}} + + +def merge_dicts(*dicts): + return reduce(merge2, dicts, {}) + + +def test_merge_dicts(): + assert merge_dicts({"a": 1}, {"b": 2}) == {"a": 1, "b": 2} + assert merge_dicts({"a": 1}, {"a": 2}) == {"a": 2} + assert merge_dicts({"a": {"b": 1}}, {"a": {"c": 2}}) == { + "a": {"b": 1, "c": 2}} + assert merge_dicts({"a": {"b": 1}}, {"a": {"b": 2}}) == {"a": {"b": 2}} + assert merge_dicts({"a": {"b": 1}}, {"a": {"b": 2}, "c": 3}) == { + "a": {"b": 2}, "c": 3} + assert merge_dicts({"a": 1}, {"b": 2}, {"c": 3}) == { + "a": 1, "b": 2, "c": 3} + assert merge_dicts({"a": 1}, {"a": 2}, {"c": 3}) == {"a": 2, "c": 3} + + +def round_up(value, multiple): + """ + Round up a value to the nearest multiple of another value. + If the value is already a multiple, it remains unchanged. + + Args: + value (int): The value to round up + multiple (int): The multiple to round up to + + Returns: + int: The rounded up value + """ + if multiple <= 1: + return value + + remainder = value % multiple + if remainder == 0: + return value + + return value + (multiple - remainder) + + +def ini_to_dict(filename): + config = configparser.ConfigParser() + config.read(filename) + + result = {} + for section in config.sections(): + result[section] = {} + for key, value in config.items(section): + result[section][key] = value + return result + + +def load_configs_merged(config_paths): + config = {} + for config_path in config_paths: + if os.path.exists(config_path): + logger.info(f"Loading configuration from {config_path}") + config = merge_dicts(config, ini_to_dict(config_path)) + return config + + +def update_guest_config(config_file: str, data: Dict): + if not os.path.exists(config_file): + config = {} + else: + with open(config_file, 'r') as f: + config = json.load(f) + config.update(data) + with open(config_file, 'w') as f: + json.dump(config, f, indent=4) + + +def gen_vm_config(vm_dir, host_port, manifest=None, os_image_hash=None): + shared_dir = os.path.join(vm_dir, 'shared') + for filename in ['config.json', '.sys-config.json']: + config_file = os.path.join(shared_dir, filename) + update_guest_config(config_file, { + "host_api_url": f"http://10.0.2.2:{host_port}/api", + "host_vsock_port": host_port + }) + if manifest: + update_guest_config(config_file, { + "vm_config": json.dumps({ + "os_image_hash": os_image_hash, + "cpu_count": manifest['vcpu'], + "memory_size": manifest['memory'] * 1024 * 1024 + }) + }) + + +@dataclass +class DstackConfig: + """Configuration for dstack client.""" + docker_registry: Optional[str] = None + default_image_name: str = '' + qemu_path: str = 'qemu-system-x86_64' + + @classmethod + def load(cls) -> 'DstackConfig': + """Load configuration from file.""" + cfgs = load_configs_merged(generate_config_paths()) + + def cfg_get(section, key, fallback): + if section in cfgs and key in cfgs[section]: + return cfgs[section][key] + return fallback + me = cls() + me.docker_registry = cfg_get('docker', 'registry', cls.docker_registry) + me.default_image_name = cfg_get( + 'image', 'default', cls.default_image_name) + me.qemu_path = cfg_get('qemu', 'path', cls.qemu_path) + return me + + +class DstackManager: + def __init__(self): + self.run_path = os.path.abspath(os.getenv('RUN_PATH', './vms')) + self.config = DstackConfig.load() + + def _generate_instance_id(self) -> str: + """Generate a random instance ID.""" + return str(uuid.uuid4()) + + def _read_compose_file(self, compose_file: str) -> str: + """Read and validate compose file.""" + if not os.path.isfile(compose_file): + raise FileNotFoundError(f"Compose file not found: {compose_file}") + with open(compose_file, 'r') as f: + return f.read() + + def _create_directories(self, work_dir: str) -> tuple[str, str]: + """Create necessary directories.""" + if os.path.exists(work_dir): + raise FileExistsError(f"The instance already exists at {work_dir}") + + shared_dir = os.path.join(work_dir, 'shared') + certs_dir = os.path.join(shared_dir, 'certs') + os.makedirs(shared_dir, exist_ok=True) + os.makedirs(certs_dir, exist_ok=True) + return shared_dir, certs_dir + + def _convert_memory_to_mb(self, memory: str) -> int: + """Convert memory string to MB.""" + if memory.upper().endswith('T'): + return int(memory[:-1]) * 1024 * 1024 + if memory.upper().endswith('G'): + return int(memory[:-1]) * 1024 + if memory.upper().endswith('M'): + return int(memory[:-1]) + return int(memory) + + def _parse_port_mapping(self, port_str: str) -> dict: + """Parse port mapping string in format 'protocol[:address]:from:to'.""" + try: + parts = port_str.split(':') + if len(parts) == 3: + proto, from_port, to_port = parts + address = "127.0.0.1" # default to localhost + elif len(parts) == 4: + proto, address, from_port, to_port = parts + else: + raise ValueError( + "Invalid port mapping format. Use 'protocol[:address]:from:to'") + + return { + "address": address, + "protocol": proto.lower(), + "from": int(from_port), + "to": int(to_port) + } + except ValueError as e: + raise ValueError(f"Invalid port mapping '{port_str}': {str(e)}") + + def setup_instance(self, args: argparse.Namespace) -> None: + """Set up a new instance with the provided configuration.""" + try: + # Generate instance ID if work_dir not provided + instance_id = os.path.basename( + args.dir) if args.dir else self._generate_instance_id() + work_dir = args.dir or os.path.join(self.run_path, instance_id) + + # Create directories + shared_dir, certs_dir = self._create_directories(work_dir) + + # Read compose file + compose_content = self._read_compose_file(args.compose_file) + + # Create app-compose.json + app_compose = { + "manifest_version": 1, + "name": "example", + "version": "1.0.0", + "features": [], + "runner": "docker-compose", + "docker_compose_file": compose_content, + "local_key_provider_enabled": args.local_key_provider, + "secure_time": False, + } + with open(os.path.join(shared_dir, 'app-compose.json'), 'w') as f: + json.dump(app_compose, f, indent=4) + # Read image metadata and create config.json + + if self.config.docker_registry: + update_guest_config(os.path.join(shared_dir, '.sys-config.json'), { + "docker_registry": self.config.docker_registry, + }) + + # Create VM manifest + memory = self._convert_memory_to_mb(str(args.memory)) + disk_size = self._convert_memory_to_mb(str(args.disk)) // 1024 + port_map = [] + if args.port: + for port_str in args.port: + port_map.append(self._parse_port_mapping(port_str)) + + if args.gpu == ['all']: + gpus = { + "attach_mode": "all", + } + elif args.gpu == ['none'] or not args.gpu: + gpus = { + "attach_mode": "listed", + "gpus": [] + } + else: + gpus = { + "attach_mode": "listed", + "gpus": [ + {"slot": gpu} for gpu in args.gpu + ] + } + gpus = self.resolve_gpus(gpus) + vm_config = { + "id": instance_id, + "name": "", + "vcpu": args.vcpus, + "gpus": gpus, + "memory": memory, + "disk_size": disk_size, + "image_path": args.image, + "image": os.path.basename(args.image.rstrip('/')), + "port_map": port_map, + "pin_numa": args.pin_numa, + "hugepages": args.hugepages, + "created_at_ms": int(datetime.now().timestamp() * 1000) + } + with open(os.path.join(work_dir, 'vm-manifest.json'), 'w') as f: + json.dump(vm_config, f, indent=4) + logger.info(f"Work directory prepared successfully at: {work_dir}") + + except Exception as e: + logger.error(f"Failed to setup instance: {str(e)}") + raise + + @staticmethod + def collect_all_gpus() -> dict: + """Collect available NVIDIA GPUs and NVSwitches.""" + try: + # Find all NVIDIA GPUs (3D controllers) + gpu_cmd = subprocess.run( + ['lspci', '-d', '10de:', '-nn'], + capture_output=True, text=True, check=True + ) + gpu_output = gpu_cmd.stdout.strip() + + gpus = [] + bridges = [] + + # Process each line of output + for line in gpu_output.split('\n'): + if not line.strip(): + continue + + slot = line.split()[0] # Bus:Device.Function + + # Extract device ID from the line + match = re.search(r'\[10de:([0-9A-Fa-f]+)\]', line) + if not match: + continue + # Check if it's a GPU (3D controller) or NVSwitch (Bridge) + if '3D controller' in line: + gpus.append({ + "slot": slot, + }) + elif 'Bridge' in line: + bridges.append({ + "slot": slot, + }) + + logger.info( + f"Found {len(gpus)} GPU(s) and {len(bridges)} NVSwitch(es)") + + return { + "attach_mode": "all", + "gpus": gpus, + "bridges": bridges + } + except subprocess.SubprocessError as e: + logger.warning(f"Failed to collect GPU information: {str(e)}") + return { + "attach_mode": "all", + "gpus": [], + "bridges": [] + } + + @staticmethod + def resolve_gpus(gpus: dict) -> dict: + """Resolve GPU slots.""" + match gpus['attach_mode']: + case 'listed': + return gpus + case 'all': + return DstackManager.collect_all_gpus() + case _: + raise ValueError( + f"Invalid GPU attach mode: {gpus['attach_mode']}") + + def run_instance(self, vm_dir: str, host_port: int, imgdir: Optional[str] = None, dry_run: bool = False) -> None: + """Run a VM instance from the specified directory. + + Args: + vm_dir: Directory containing the VM configuration + dry_run: Whether to run in dry run mode + """ + + manifest_path = os.path.join(vm_dir, 'vm-manifest.json') + if not os.path.exists(manifest_path): + raise ValueError(f"VM manifest not found in {vm_dir}") + + with open(manifest_path, 'r') as f: + manifest = json.load(f) + + if dry_run: + print("Manifest:") + print(json.dumps(manifest, indent=4)) + + # Get image path and metadata + image_path = manifest.get('image_path') or os.path.join( + imgdir, manifest['image']) + img_metadata_path = os.path.join(image_path, 'metadata.json') + + if not os.path.exists(img_metadata_path): + raise ValueError( + f"Image metadata not found at {img_metadata_path}") + + with open(img_metadata_path, 'r') as f: + img_metadata = json.load(f) + + os_image_hash = open(os.path.join( + image_path, 'digest.txt'), 'r').read().strip() + gen_vm_config(vm_dir, host_port, manifest, os_image_hash) + + mem_gb = manifest['memory'] // 1024 + vcpu_count = manifest['vcpu'] + disk_size = manifest['disk_size'] + + vda = os.path.join(vm_dir, 'hda.img') + config_dir = os.path.join(vm_dir, 'shared') + + # Create disk if it doesn't exist + if not os.path.exists(vda): + subprocess.run(['qemu-img', 'create', '-f', 'qcow2', + vda, f"{disk_size}G"], check=True) + + cid = random.randint(1, 10000) + 3 + + # Prepare QEMU command + cmd_args = [] + rootfs_image = os.path.join(image_path, img_metadata['rootfs']) + if rootfs_image.endswith('.img.verity'): + cmd_args.extend([ + '-drive', f'file={rootfs_image},if=none,id=virtio-disk0,format=raw', + '-device', 'virtio-blk-pci,drive=virtio-disk0', + ]) + elif rootfs_image.endswith('.img'): + cmd_args.extend(['-cdrom', rootfs_image]) + else: + raise ValueError( + f"Unsupported rootfs image format: {rootfs_image}") + cmd_args.extend(['-drive', f'file={vda},if=none,id=virtio-disk1']) + cmd_args.extend(['-device', 'virtio-blk-pci,drive=virtio-disk1']) + + # Add network configuration + port_args = [] + for port_map in manifest.get('port_map', []): + protocol = port_map.get('protocol', 'tcp') + bind_address = port_map.get('address', '127.0.0.1') + host_port = port_map['from'] + vm_port = port_map['to'] + port_args.append( + f"hostfwd={protocol}:{bind_address}:{host_port}-:{vm_port}") + cmd_args.extend([ + '-device', 'virtio-net-pci,netdev=nic0_td', + '-netdev', f"user,id=nic0_td{','+','.join(port_args) if len(port_args) > 0 else ''}" + ]) + + # Handle GPUs + gpus_cfg = manifest.get('gpus') or {} + gpus = gpus_cfg.get('gpus') or [] + bridges = gpus_cfg.get('bridges') or [] + dev_num = 1 + hugepages = manifest.get('hugepages', False) + if hugepages: + numa_nodes = {} + if gpus: + for dev in gpus: + node = numa_node_of_device(dev['slot']) + if node not in numa_nodes: + numa_nodes[node] = 0 + numa_nodes[node] += 1 + else: + numa_nodes[0] = 0 + n_numa = len(numa_nodes) + # Round up cpu cores and memory to multiple times of numa nodes + vcpu_count = round_up(vcpu_count, n_numa) + mem_gb = round_up(mem_gb, n_numa) + vcpu_per_node = vcpu_count // n_numa + mem_per_node = mem_gb // n_numa + + bus_nr = 5 + for ind, (node, count) in enumerate(numa_nodes.items()): + cmd_args.extend([ + '-numa', f'node,nodeid={ind},cpus={ind * vcpu_per_node}-{(ind + 1) * vcpu_per_node - 1},memdev=mem{ind}', + '-object', f'memory-backend-file,id=mem{ind},size={mem_per_node}G,mem-path=/dev/hugepages,share=on,prealloc=yes,host-nodes={node},policy=bind', + '-device', f'pxb-pcie,id=pcie.node{node},bus=pcie.0,addr={0xa + ind},numa_node={ind},bus_nr={bus_nr}' + ]) + bus_nr += count + 1 + if gpus: + cmd_args.extend(['-object', 'iommufd,id=iommufd0']) + if not hugepages: + for dev in gpus: + slot = dev['slot'] + cmd_args.extend([ + '-device', f'pcie-root-port,id=pci.{dev_num},bus=pcie.0,chassis={dev_num}', + '-device', f'vfio-pci,host={slot},bus=pci.{dev_num},iommufd=iommufd0', + ]) + dev_num += 1 + else: + for dev in gpus: + slot = dev['slot'] + node = numa_node_of_device(slot) + cmd_args.extend([ + '-device', f'pcie-root-port,id=pci.{dev_num},bus=pcie.node{node},chassis={dev_num}', + '-device', f'vfio-pci,host={slot},bus=pci.{dev_num},iommufd=iommufd0', + ]) + dev_num += 1 + for bridge in bridges: + slot = bridge['slot'] + cmd_args.extend([ + '-device', f'pcie-root-port,id=pci.{dev_num},bus=pcie.0,chassis={dev_num}', + '-device', f'vfio-pci,host={slot},bus=pci.{dev_num},iommufd=iommufd0', + ]) + dev_num += 1 + # Add kernel command line + cmd_args.extend(['-append', img_metadata['cmdline']]) + + base_args = [ + self.config.qemu_path, + '-accel', 'kvm', + '-m', f'{mem_gb}G', + '-smp', str(vcpu_count), + '-cpu', 'host', + '-machine', 'q35,kernel_irqchip=split,confidential-guest-support=tdx,hpet=off', + '-object', 'tdx-guest,id=tdx', + '-nographic', + '-nodefaults', + '-chardev', 'stdio,id=ser0,signal=on', + '-serial', 'chardev:ser0', + '-kernel', os.path.join(image_path, img_metadata['kernel']), + '-initrd', os.path.join(image_path, img_metadata['initrd']), + '-bios', os.path.join(image_path, img_metadata['bios']), + '-virtfs', f'local,path={config_dir},mount_tag=host-shared,readonly=off,security_model=mapped,id=virtfs0', + '-device', f'vhost-vsock-pci,guest-cid={cid}', + ] + + pin_numa = manifest.get('pin_numa', False) + if pin_numa: + if gpus: + numa_node = numa_node_of_device(gpus[0]['slot']) + else: + numa_node = 0 + cpus = open( + f'/sys/devices/system/node/node{numa_node}/cpulist').read().strip() + base_args = ['taskset', '-c', cpus] + base_args + cmd = base_args + cmd_args + print(" \n".join(cmd)) + if dry_run: + return + # Run the command + try: + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Failed to start VM: {e}") + + +def numa_node_of_device(pci_slot): + """ + Get the NUMA node associated with a PCI device. + + Args: + pci_slot (str): PCI slot in format "domain:bus:device.function" (e.g., "0000:ab:00.0") + + Returns: + int: NUMA node number, or -1 if device doesn't exist or has no NUMA affinity + """ + # Normalize the PCI slot format if needed + if not pci_slot.startswith("0000:"): + pci_slot = f"0000:{pci_slot}" + # Try to read NUMA node from sysfs + numa_path = f"/sys/bus/pci/devices/{pci_slot}/numa_node" + + with open(numa_path, 'r') as f: + numa_node = int(f.read().strip()) + return numa_node + + +def list_available_gpus() -> None: + """List available NVIDIA GPUs.""" + try: + # Run lspci with verbose output to get detailed information + result = subprocess.run( + ['lspci', '-vvk'], capture_output=True, text=True) + output_lines = result.stdout.split('\n') + + # Find all GPU entries and their details + gpu_blocks = [] + current_block = [] + in_gpu_block = False + + for line in output_lines: + if 'NVIDIA' in line and '3D controller' in line: + # Start of a new GPU block + if current_block: + gpu_blocks.append(current_block) + current_block = [line] + in_gpu_block = True + elif in_gpu_block: + if line.strip() == '' or (line[0] != '\t' and line[0] != ' ' and len(current_block) > 1): + # End of the current block + gpu_blocks.append(current_block) + current_block = [] + in_gpu_block = False + else: + # Continue adding lines to the current block + current_block.append(line) + + # Add the last block if it exists + if current_block: + gpu_blocks.append(current_block) + + if gpu_blocks: + print("\nAvailable GPU IDs:") + print("ID Numa Node In Use Description") + print("-------------------------------------") + + for block in gpu_blocks: + # Extract device ID from the first line + device_id = block[0].split()[0] + description = block[0].split(':', 2)[2].strip() + + # Check if GPU is in use by examining Control line and Latency + in_use = False + for line in block: + if 'Control:' in line and 'I/O+' in line and 'BusMaster+' in line: + in_use = True + elif 'Latency:' in line: + in_use = True + + status = "Yes" if in_use else "No" + node = numa_node_of_device(device_id) + print(f"{device_id} {node} {status:8} {description}") + print() + except subprocess.SubprocessError as e: + logger.warning(f"Could not list GPU devices: {str(e)}") + + +def start_server(dir: str, kp_port: int): + config = host_api.ServerConfig( + vm_dir=dir, kp_address="127.0.0.1", kp_port=kp_port) + api, host_port = host_api.create_http_server(config) + print(f"Starting HTTP server on localhost:{host_port}") + thread = threading.Thread(target=api.serve_forever, daemon=True) + thread.host_port = host_port + thread.start() + return thread + + +def tag_vfio(): + """ + Tag NVIDIA GPUs and NVSwitches for VFIO passthrough. + Detects NVIDIA devices and configures them for VFIO passthrough. + """ + logging.info("==> Detecting NVIDIA GPUs and NVSwitches") + + try: + # Use a more structured approach to detect devices + devices = detect_nvidia_devices() + + if not devices['gpus'] and not devices['switches']: + logging.error("No NVIDIA GPUs or NVSwitches found. Exiting.") + return + + ngpu = len(devices['gpus']) + nsw = len(devices['switches']) + + logging.info(f"Found {ngpu} GPU(s)") + logging.info(f"Found {nsw} NVSwitch(es)") + + # Load VFIO modules + logging.info("==> Loading VFIO modules") + load_vfio_modules() + + # Tag devices for VFIO passthrough + logging.info("==> Tagging devices for VFIO passthrough") + + # Deduplicate device IDs + unique_dev_ids = {} + for device_type, device_list in devices.items(): + device_name = "GPU" if device_type == "gpus" else "NVSwitch" + for device in device_list: + dev_id = device['dev_id'] + if dev_id not in unique_dev_ids: + unique_dev_ids[dev_id] = device_name + # If the device ID is already in the dict but with a different type, + # we'll keep the original type for simplicity + + # Process unique device IDs + for dev_id, device_name in unique_dev_ids.items(): + tag_device_for_vfio(dev_id, device_name) + + logging.info("VFIO passthrough setup complete") + + except Exception as e: + logging.error(f"Failed to enable VFIO passthrough: {e}") + + +def detect_nvidia_devices(): + """ + Detect NVIDIA GPUs and NVSwitches in the system. + + Returns: + dict: Dictionary with 'gpus' and 'switches' keys, each containing a list of + dictionaries with 'dev_id' for each device. + """ + devices = { + 'gpus': [], + 'switches': [] + } + + try: + # Run lspci once and parse the output + lspci_output = subprocess.check_output( + "lspci -d 10de: -nn", + shell=True, text=True + ).strip().split('\n') + + for line in lspci_output: + if not line: + continue + + # Extract Device ID + dev_id_match = re.search(r'\[10de:([0-9A-Fa-f]+)\]', line) + if not dev_id_match: + continue + + dev_id = dev_id_match.group(1) + + # Categorize device + if '3D controller' in line: + devices['gpus'].append({'dev_id': dev_id}) + elif 'Bridge' in line: + devices['switches'].append({'dev_id': dev_id}) + + except subprocess.CalledProcessError as e: + logging.error(f"Failed to detect NVIDIA devices: {e}") + + return devices + + +def load_vfio_modules(): + """ + Load the VFIO kernel modules required for device passthrough. + Assumes the script is run with appropriate permissions. + + Raises: + RuntimeError: If modules cannot be loaded + """ + try: + subprocess.run(["modprobe", "vfio"], check=True) + subprocess.run(["modprobe", "vfio_pci"], check=True) + except subprocess.CalledProcessError as e: + logging.error(f"Failed to load VFIO modules: {e}") + raise RuntimeError("Failed to load VFIO modules") from e + + +def tag_device_for_vfio(dev_id, device_type): + """ + Tag a PCI device for VFIO passthrough. + + Args: + dev_id (str): Device ID + device_type (str): Type of device (GPU or NVSwitch) + """ + logging.info(f"Tagging {device_type} (DevID=10de:{dev_id})") + + new_id_path = "/sys/bus/pci/drivers/vfio-pci/new_id" + remove_id_path = "/sys/bus/pci/drivers/vfio-pci/remove_id" + device_id_value = f"10de {dev_id}" + + try: + # Directly write to the sysfs file + write_to_sysfs(new_id_path, device_id_value) + except Exception: + try: + # Remove the ID first in case it's already there + try: + write_to_sysfs(remove_id_path, device_id_value) + except Exception: + # Ignore errors when removing + pass + + # Try adding again + write_to_sysfs(new_id_path, device_id_value) + except Exception as e: + logging.error(f"Failed to tag {device_type}: {e}") + + +def write_to_sysfs(path, value): + """ + Write a value directly to a sysfs file. + Assumes the script is run with appropriate permissions. + + Args: + path (str): Path to the sysfs file + value (str): Value to write + + Raises: + IOError: If the write fails + """ + with open(path, 'w') as f: + f.write(value) + + +def main(): + parser = argparse.ArgumentParser(description='Legacy direct-QEMU dstack VM runner') + subparsers = parser.add_subparsers(dest='command', help='Commands') + + # Setup command + setup_parser = subparsers.add_parser('new', help='Setup a new instance') + setup_parser.add_argument('compose_file', type=str, + help='Docker compose file') + setup_parser.add_argument('-o', '--dir', type=str, help='Work directory') + setup_parser.add_argument('-i', '--image', type=str, help='VM image path') + setup_parser.add_argument( + '-c', '--vcpus', type=int, default=1, help='Number of vCPUs') + setup_parser.add_argument( + '-m', '--memory', type=str, default='2G', help='Memory size (e.g., 1G, 512M)') + setup_parser.add_argument('-d', '--disk', type=str, + default='20G', help='Disk size (e.g., 20G)') + setup_parser.add_argument('-g', '--gpu', type=str, + action='append', help='GPU device') + setup_parser.add_argument('-p', '--port', action='append', type=str, + help='Port mapping in format: protocol[:address]:from:to') + setup_parser.add_argument('--local-key-provider', '--lkp', + action='store_true', help='Enable local key provider') + setup_parser.add_argument( + '--pin-numa', action='store_true', help='Pin vCPUs to NUMA node') + setup_parser.add_argument( + '--hugepages', action='store_true', help='Enable hugepages') + + # Start command + start_parser = subparsers.add_parser('run', help='Start an instance') + start_parser.add_argument('dir', type=str, help='Work directory') + start_parser.add_argument('--imgdir', type=str, help='The image directory') + start_parser.add_argument( + '--kp-port', type=int, default=3443, help='The key provider listening port') + start_parser.add_argument( + '--dry-run', action='store_true', help='Run in dry run mode') + + # List Gpus command + subparsers.add_parser('lsgpu', help='List available GPUs') + + # Tag VFIO command + subparsers.add_parser( + 'tag-vfio', help='Tag NVIDIA GPUs and NVSwitches for VFIO passthrough') + + # Run the host server only + serve_parser = subparsers.add_parser( + 'serve', help='Run the host server only') + serve_parser.add_argument('dir', type=str, help='Work directory') + serve_parser.add_argument( + '--kp-port', type=int, default=3443, help='The key provider listening port') + + args = parser.parse_args() + + if args.command == 'new': + manager = DstackManager() + manager.setup_instance(args) + elif args.command == 'run': + manager = DstackManager() + thread = start_server(args.dir, args.kp_port) + manager.run_instance(args.dir, thread.host_port, + imgdir=args.imgdir, dry_run=args.dry_run) + elif args.command == 'lsgpu': + list_available_gpus() + elif args.command == 'tag-vfio': + tag_vfio() + elif args.command == 'serve': + thread = start_server(args.dir, args.kp_port) + gen_vm_config(args.dir, thread.host_port) + thread.join() + else: + parser.print_help() + + +if __name__ == '__main__': + main()